diff --git a/app/blueprints/api/endpoints.py b/app/blueprints/api/endpoints.py index 4beec0af..579eacb8 100644 --- a/app/blueprints/api/endpoints.py +++ b/app/blueprints/api/endpoints.py @@ -22,7 +22,7 @@ from app import csrf from app.utils.markdown import render_markdown from app.models import Tag, PackageState, PackageType, Package, db, PackageRelease, Permission, ForumTopic, MinetestRelease, APIToken, PackageScreenshot, License, ContentWarning, User from app.querybuilder import QueryBuilder -from app.utils import is_package_page +from app.utils import is_package_page, get_int_or_abort from . import bp from .auth import is_api_authd from .support import error, api_create_vcs_release, api_create_zip_release, api_create_screenshot, api_order_screenshots, api_edit_package @@ -390,5 +390,14 @@ def homepage(): @bp.route("/api/minetest_versions/") def versions(): + protocol_version = request.args.get("protocol_version") + engine_version = request.args.get("engine_version") + if protocol_version or engine_version: + rel = MinetestRelease.get(engine_version, get_int_or_abort(protocol_version)) + if rel is None: + error(404, "No releases found") + + return jsonify(rel.getAsDictionary()) + return jsonify([rel.getAsDictionary() \ for rel in MinetestRelease.query.all() if rel.getActual() is not None]) diff --git a/app/default_data.py b/app/default_data.py index 333dd939..ba5c1bc3 100644 --- a/app/default_data.py +++ b/app/default_data.py @@ -15,6 +15,8 @@ def populate(session): session.add(MinetestRelease("0.4.16/17", 32)) session.add(MinetestRelease("5.0", 37)) session.add(MinetestRelease("5.1", 38)) + session.add(MinetestRelease("5.2", 39)) + session.add(MinetestRelease("5.3", 39)) tags = {} for tag in ["Inventory", "Mapgen", "Building", diff --git a/app/models/packages.py b/app/models/packages.py index 35f762c8..cff55b36 100644 --- a/app/models/packages.py +++ b/app/models/packages.py @@ -796,7 +796,8 @@ class MinetestRelease(db.Model): if protocol_num: # Find the closest matching release - return MinetestRelease.query.order_by(db.desc(MinetestRelease.protocol)) \ + return MinetestRelease.query.order_by(db.desc(MinetestRelease.protocol), + db.desc(MinetestRelease.id)) \ .filter(MinetestRelease.protocol <= protocol_num).first() return None diff --git a/app/tests/test_api.py b/app/tests/test_api.py index e5e8aa0e..8d19adb4 100644 --- a/app/tests/test_api.py +++ b/app/tests/test_api.py @@ -1,31 +1,9 @@ from app.default_data import populate_test_data from app.models import db, Package, PackageState -from .utils import is_str, is_int, is_optional, parse_json +from .utils import parse_json, validate_package_list from .utils import client # noqa -def validate_package_list(packages, strict=False): - valid_keys = { - "author", "name", "release", - "short_description", "thumbnail", - "title", "type" - } - - for package in packages: - assert set(package.keys()).issubset(valid_keys) - - assert is_str(package.get("author")) - assert is_str(package.get("name")) - if strict: - assert is_int(package.get("release")) - else: - assert is_optional(int, package.get("release")) - assert is_str(package.get("short_description")) - assert is_optional(str, package.get("thumbnail")) - assert is_str(package.get("title")) - assert is_str(package.get("type")) - - def test_packages_empty(client): """Start with a blank database.""" @@ -66,40 +44,3 @@ def test_packages_with_query(client): assert (packages[0]["name"] == "food" and packages[1]["name"] == "food_sweet") or \ (packages[1]["name"] == "food" and packages[0]["name"] == "food_sweet") - - -def test_packages_with_protocol_high(client): - """Start with a test database.""" - - populate_test_data(db.session) - db.session.commit() - - rv = client.get("/api/packages/?protocol_version=100") - - packages = parse_json(rv.data) - - for package in packages: - assert package["name"] != "mesecons" - assert package["name"] != "handholds" - - assert len(packages) == 4 - - validate_package_list(packages, True) - - -def test_packages_with_protocol_low(client): - """Start with a test database.""" - - populate_test_data(db.session) - db.session.commit() - - rv = client.get("/api/packages/?protocol_version=20") - - packages = parse_json(rv.data) - - assert len(packages) == 4 - - for package in packages: - assert package["name"] != "awards" - - validate_package_list(packages, True) diff --git a/app/tests/test_releases_queries.py b/app/tests/test_releases_queries.py new file mode 100644 index 00000000..244d9aa3 --- /dev/null +++ b/app/tests/test_releases_queries.py @@ -0,0 +1,218 @@ +from typing import List, Tuple, Optional + +from app.default_data import populate_test_data +from app.models import db, License, PackageType, User, Package, PackageState, PackageRelease, MinetestRelease +from .utils import parse_json, validate_package_list +from .utils import client # noqa + + +def make_package(name: str, versions: List[Tuple[Optional[str], Optional[str]]]) -> List[int]: + license = License.query.filter_by(name="MIT").first() + author = User.query.first() + + mod = Package() + mod.state = PackageState.APPROVED + mod.name = name.lower() + mod.title = name + mod.license = license + mod.media_license = license + mod.type = PackageType.MOD + mod.author = author + mod.short_desc = "The content library should not be used yet as it is still in alpha" + mod.desc = "This is the long desc" + db.session.add(mod) + + rels = [] + + for (minv, maxv) in versions: + rel = PackageRelease() + rel.package = mod + rel.title = "test" + rel.url = "https://github.com/ezhh/handholds/archive/master.zip" + + if minv: + rel.min_rel = MinetestRelease.query.filter_by(name=minv).first() + assert rel.min_rel + if maxv: + rel.max_rel = MinetestRelease.query.filter_by(name=maxv).first() + assert rel.max_rel + + rel.approved = True + db.session.add(rel) + rels.append(rel) + + db.session.flush() + + return [rel.id for rel in rels] + + +def test_packages_with_protocol_multi_high(client): + """Start with a test database.""" + + populate_test_data(db.session) + db.session.commit() + + rv = client.get("/api/packages/?protocol_version=100") + + packages = parse_json(rv.data) + + for package in packages: + assert package["name"] != "mesecons" + assert package["name"] != "handholds" + + assert len(packages) == 4 + + validate_package_list(packages, True) + + +def test_packages_with_protocol_multi_low(client): + """Start with a test database.""" + + populate_test_data(db.session) + db.session.commit() + + rv = client.get("/api/packages/?protocol_version=20") + + packages = parse_json(rv.data) + + assert len(packages) == 4 + + for package in packages: + assert package["name"] != "awards" + + validate_package_list(packages, True) + + +def test_packages_with_protocol_max_ver(client): + """Start with a blank database.""" + + make_package("Bob", [ (None, "5.0") ]) + db.session.commit() + + packages = parse_json(client.get("/api/packages/?protocol_version=20").data) + assert len(packages) == 1 + assert packages[0]["name"] == "bob" + + packages = parse_json(client.get("/api/packages/?protocol_version=32").data) + assert len(packages) == 1 + assert packages[0]["name"] == "bob" + + packages = parse_json(client.get("/api/packages/?protocol_version=37").data) + assert len(packages) == 1 + assert packages[0]["name"] == "bob" + + packages = parse_json(client.get("/api/packages/?protocol_version=38").data) + assert len(packages) == 0 + + packages = parse_json(client.get("/api/packages/?protocol_version=40").data) + assert len(packages) == 0 + + validate_package_list(packages, True) + + +def test_packages_with_protocol_min_ver(client): + """Start with a blank database.""" + + make_package("Bob", [("5.0", None)]) + db.session.commit() + + packages = parse_json(client.get("/api/packages/?protocol_version=20").data) + assert len(packages) == 0 + + packages = parse_json(client.get("/api/packages/?protocol_version=32").data) + assert len(packages) == 0 + + packages = parse_json(client.get("/api/packages/?protocol_version=37").data) + assert len(packages) == 1 + assert packages[0]["name"] == "bob" + + packages = parse_json(client.get("/api/packages/?protocol_version=38").data) + assert len(packages) == 1 + assert packages[0]["name"] == "bob" + + packages = parse_json(client.get("/api/packages/?protocol_version=40").data) + assert len(packages) == 1 + assert packages[0]["name"] == "bob" + + validate_package_list(packages, True) + + +def test_packages_with_protocol_engine_ver(client): + """Start with a blank database.""" + + make_package("Bob", [("5.3", None)]) + db.session.commit() + + packages = parse_json(client.get("/api/packages/?protocol_version=20&engine_version=4.0").data) + assert len(packages) == 0 + + packages = parse_json(client.get("/api/packages/?protocol_version=38&engine_version=5.1").data) + assert len(packages) == 0 + + packages = parse_json(client.get("/api/packages/?protocol_version=39&engine_version=5.2").data) + assert len(packages) == 0 + + packages = parse_json(client.get("/api/packages/?protocol_version=39&engine_version=5.3").data) + assert len(packages) == 1 + assert packages[0]["name"] == "bob" + + packages = parse_json(client.get("/api/packages/?protocol_version=40&engine_version=5.6").data) + assert len(packages) == 1 + assert packages[0]["name"] == "bob" + + validate_package_list(packages, True) + + +def test_packages_with_protocol_exact(client): + """Start with a blank database.""" + + make_package("Bob", [("5.0", "5.0")]) + db.session.commit() + + packages = parse_json(client.get("/api/packages/?protocol_version=20").data) + assert len(packages) == 0 + + packages = parse_json(client.get("/api/packages/?protocol_version=32").data) + assert len(packages) == 0 + + packages = parse_json(client.get("/api/packages/?protocol_version=37").data) + assert len(packages) == 1 + assert packages[0]["name"] == "bob" + + packages = parse_json(client.get("/api/packages/?protocol_version=38").data) + assert len(packages) == 0 + + packages = parse_json(client.get("/api/packages/?protocol_version=40").data) + assert len(packages) == 0 + + validate_package_list(packages, True) + + +def test_packages_with_protocol_options(client): + """Start with a blank database.""" + + rels = make_package("Bob", [(None, "0.4.16/17"), ("5.1", "5.1")]) + db.session.commit() + + packages = parse_json(client.get("/api/packages/?protocol_version=20").data) + assert len(packages) == 1 + assert packages[0]["name"] == "bob" + assert packages[0]["release"] == rels[0] + + packages = parse_json(client.get("/api/packages/?protocol_version=32").data) + assert len(packages) == 1 + assert packages[0]["name"] == "bob" + assert packages[0]["release"] == rels[0] + + packages = parse_json(client.get("/api/packages/?protocol_version=37").data) + assert len(packages) == 0 + + packages = parse_json(client.get("/api/packages/?protocol_version=38").data) + assert len(packages) == 1 + assert packages[0]["name"] == "bob" + assert packages[0]["release"] == rels[1] + + packages = parse_json(client.get("/api/packages/?protocol_version=40").data) + assert len(packages) == 0 + + validate_package_list(packages, True) diff --git a/app/tests/utils.py b/app/tests/utils.py index be2b8690..4afaaabf 100644 --- a/app/tests/utils.py +++ b/app/tests/utils.py @@ -43,3 +43,25 @@ def client(): yield client app.config["TESTING"] = False + + +def validate_package_list(packages, strict=False): + valid_keys = { + "author", "name", "release", + "short_description", "thumbnail", + "title", "type" + } + + for package in packages: + assert set(package.keys()).issubset(valid_keys) + + assert is_str(package.get("author")) + assert is_str(package.get("name")) + if strict: + assert is_int(package.get("release")) + else: + assert is_optional(int, package.get("release")) + assert is_str(package.get("short_description")) + assert is_optional(str, package.get("thumbnail")) + assert is_str(package.get("title")) + assert is_str(package.get("type"))