From 61d500fdbc67cff27fe5a3e1071cb559c1654406 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=9Clgen=20Sar=C4=B1kavak?= Date: Tue, 11 Aug 2026 15:33:59 +0300 Subject: [PATCH 1/3] Apply 4-spaces indentation to Python code Applied using autopep8 and E101 rule. https://peps.python.org/pep-0008/#indentation > Use 4 spaces per indentation level. Relates to #80 --- scripts/fetch_updates.py | 183 ++++++++++++++++++------------------ scripts/to_contribs_txt.py | 114 +++++++++++----------- scripts/to_sources_jsons.py | 76 +++++++-------- 3 files changed, 187 insertions(+), 186 deletions(-) diff --git a/scripts/fetch_updates.py b/scripts/fetch_updates.py index a6bc461..f8bdf00 100644 --- a/scripts/fetch_updates.py +++ b/scripts/fetch_updates.py @@ -11,114 +11,115 @@ def update_contribution(contribution, props): - datetime_today = datetime.now(UTC).strftime('%Y-%m-%dT%H:%M:%S%z') - contribution['lastUpdated'] = datetime_today - if 'previousVersions' not in contribution: - contribution['previousVersions'] = [] - contribution['previousVersions'].append(contribution['prettyVersion']) - - # update from online - for field in props.keys(): - # process category list - if field == 'categories': - if props[field]: - contribution[field] = sorted(props[field].strip('"').split(',')) - else: - contribution[field] = [] - else: - contribution[field] = props[field] + datetime_today = datetime.now(UTC).strftime('%Y-%m-%dT%H:%M:%S%z') + contribution['lastUpdated'] = datetime_today + if 'previousVersions' not in contribution: + contribution['previousVersions'] = [] + contribution['previousVersions'].append(contribution['prettyVersion']) + + # update from online + for field in props.keys(): + # process category list + if field == 'categories': + if props[field]: + contribution[field] = sorted(props[field].strip('"').split(',')) + else: + contribution[field] = [] + else: + contribution[field] = props[field] + + if 'download' not in contribution: + contribution['download'] = contribution['source'][:contribution['source'].rfind('.')] + '.zip' - if 'download' not in contribution: - contribution['download'] = contribution['source'][:contribution['source'].rfind('.')] + '.zip' def log_broken(contribution, msg): - if contribution['status'] == 'VALID': - contribution['status'] = 'BROKEN' - if 'log' not in contribution: - contribution['log'] = [] - contribution['log'].append(msg) + if contribution['status'] == 'VALID': + contribution['status'] = 'BROKEN' + if 'log' not in contribution: + contribution['log'] = [] + contribution['log'].append(msg) def process_contribution(item): - index, contribution = item + index, contribution = item - date_today = datetime.now(UTC).strftime('%Y-%m-%d') - this_version = '0' + date_today = datetime.now(UTC).strftime('%Y-%m-%d') + this_version = '0' - if contribution['status'] != 'DEPRECATED': - # compare version to what is at url. If has changed, update contribution to - # what is online - if 'version' in contribution: - this_version = contribution['version'] + if contribution['status'] != 'DEPRECATED': + # compare version to what is at url. If has changed, update contribution to + # what is online + if 'version' in contribution: + this_version = contribution['version'] - try: - properties_raw = read_properties_txt(contribution['source']) - except FileNotFoundError as e: - log_broken(contribution, f'file not found, {e}, {date_today}') - return index, contribution - except Exception: - log_broken(contribution, f'url timeout, {date_today}') - return index, contribution + try: + properties_raw = read_properties_txt(contribution['source']) + except FileNotFoundError as e: + log_broken(contribution, f'file not found, {e}, {date_today}') + return index, contribution + except Exception: + log_broken(contribution, f'url timeout, {date_today}') + return index, contribution - try: - props = validate_existing(parse_text(properties_raw)) - except Exception: - log_broken(contribution, f'invalid file, {date_today}') - return index, contribution + try: + props = validate_existing(parse_text(properties_raw)) + except Exception: + log_broken(contribution, f'invalid file, {date_today}') + return index, contribution - # some library files have field lastUpdated. This also exists in the database, but is defined - # by our scripts, so remove this field. - contribution.pop('lastUpdated', None) + # some library files have field lastUpdated. This also exists in the database, but is defined + # by our scripts, so remove this field. + contribution.pop('lastUpdated', None) - contribution['status'] = 'VALID' + contribution['status'] = 'VALID' - if props['version'] != this_version: - # update from online - update_contribution(contribution, props) - return index, contribution + if props['version'] != this_version: + # update from online + update_contribution(contribution, props) + return index, contribution def process_all(contributions_list): - total = len(contributions_list) - completed = 0 - print(f"Starting processing of {total} contributions...") + total = len(contributions_list) + completed = 0 + print(f"Starting processing of {total} contributions...") - with Pool(processes=256) as pool: - for index, contribution in pool.imap_unordered(process_contribution, enumerate(contributions_list)): - contributions_list[index] = contribution - completed += 1 - print(f"Progress: {completed}/{total} ({(completed / total * 100):.1f}%)") + with Pool(processes=256) as pool: + for index, contribution in pool.imap_unordered(process_contribution, enumerate(contributions_list)): + contributions_list[index] = contribution + completed += 1 + print(f"Progress: {completed}/{total} ({(completed / total * 100):.1f}%)") if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument('--index') - args = parser.parse_args() - - index = 'all' - if args.index: - index = args.index - - database_file = pathlib.Path(__file__).parent.parent / 'contributions.yaml' - - # read in database yaml file - yaml = YAML() - with open(database_file, 'r') as db: - data = yaml.load(db) - - contributions_list = data['contributions'] - - if index == 'all': - process_all(contributions_list) - print("All processing complete") - else: - # update only contribution with id==index - contribution = next((x for x in contributions_list if x['id'] == int(index)), None) - print(contribution) - process_contribution((index, contribution)) - print(contribution) - - # write all contributions to database file - yaml = YAML() - with open(database_file, 'w') as outfile: - yaml.dump({"contributions": contributions_list}, outfile) + parser = argparse.ArgumentParser() + parser.add_argument('--index') + args = parser.parse_args() + + index = 'all' + if args.index: + index = args.index + + database_file = pathlib.Path(__file__).parent.parent / 'contributions.yaml' + + # read in database yaml file + yaml = YAML() + with open(database_file, 'r') as db: + data = yaml.load(db) + + contributions_list = data['contributions'] + + if index == 'all': + process_all(contributions_list) + print("All processing complete") + else: + # update only contribution with id==index + contribution = next((x for x in contributions_list if x['id'] == int(index)), None) + print(contribution) + process_contribution((index, contribution)) + print(contribution) + + # write all contributions to database file + yaml = YAML() + with open(database_file, 'w') as outfile: + yaml.dump({"contributions": contributions_list}, outfile) diff --git a/scripts/to_contribs_txt.py b/scripts/to_contribs_txt.py index 6bc53b6..05be9f5 100644 --- a/scripts/to_contribs_txt.py +++ b/scripts/to_contribs_txt.py @@ -19,80 +19,80 @@ def read_contribs_text(filepath): - contribs_list = [] - this_contrib = {} - contrib_empty = True - contrib_field_counts = defaultdict(int) + contribs_list = [] + this_contrib = {} + contrib_empty = True + contrib_field_counts = defaultdict(int) - with open(filepath, 'r') as f: - for line in f.readlines(): - if line.strip() == "": - if not contrib_empty: - for key in list(this_contrib.keys()): - contrib_field_counts[key] += 1 - contribs_list.append(this_contrib) - this_contrib = {} - contrib_empty = True + with open(filepath, 'r') as f: + for line in f.readlines(): + if line.strip() == "": + if not contrib_empty: + for key in list(this_contrib.keys()): + contrib_field_counts[key] += 1 + contribs_list.append(this_contrib) + this_contrib = {} + contrib_empty = True - str_index = line.find("=") # capture first equals, - if str_index >= 0: - field, value = line.split("=", 1) - this_contrib[field.strip()] = value.strip() - contrib_empty = False + str_index = line.find("=") # capture first equals, + if str_index >= 0: + field, value = line.split("=", 1) + this_contrib[field.strip()] = value.strip() + contrib_empty = False - with open("contribs_txt_field_counts.json", 'w') as f: - json.dump(contrib_field_counts, f) + with open("contribs_txt_field_counts.json", 'w') as f: + json.dump(contrib_field_counts, f) - return contribs_list + return contribs_list def preprocess_contributions() -> List: - all_contributions = get_valid_contributions() + all_contributions = get_valid_contributions() - # sort contributions list by type - def sort_key(d): - return type_list.index(d['type']) - all_contributions = sorted(all_contributions, key=sort_key) + # sort contributions list by type + def sort_key(d): + return type_list.index(d['type']) + all_contributions = sorted(all_contributions, key=sort_key) - return all_contributions + return all_contributions def write_contribs(all_contributions, fh): - for contribution in all_contributions: - fh.write(contribution['type'] + '\n') - for field in contribs_fields_list: - if field in contribution: - if field == 'id': - fh.write(f'{field}={contribution[field]:03}\n') - elif field == 'categories': - if contribution['type'] == 'library': - fh.write(f'{field}={",".join(contribution[field]) if contribution[field] else ""}\n') - else: - # categories are only relevant for libraries, except for examples with "Books" as category - if contribution[field] and 'Books' in contribution[field]: - fh.write(f'{field}={",".join(contribution[field]) if contribution[field] else ""}\n') - else: - fh.write(f'{field}=\n') - elif field == 'compatibleModesList': - fh.write(f'modes={contribution[field]}\n') - else: - fh.write(f'{field}={"" if contribution[field] is None else contribution[field]}\n') - fh.write('\n') + for contribution in all_contributions: + fh.write(contribution['type'] + '\n') + for field in contribs_fields_list: + if field in contribution: + if field == 'id': + fh.write(f'{field}={contribution[field]:03}\n') + elif field == 'categories': + if contribution['type'] == 'library': + fh.write(f'{field}={",".join(contribution[field]) if contribution[field] else ""}\n') + else: + # categories are only relevant for libraries, except for examples with "Books" as category + if contribution[field] and 'Books' in contribution[field]: + fh.write(f'{field}={",".join(contribution[field]) if contribution[field] else ""}\n') + else: + fh.write(f'{field}=\n') + elif field == 'compatibleModesList': + fh.write(f'modes={contribution[field]}\n') + else: + fh.write(f'{field}={"" if contribution[field] is None else contribution[field]}\n') + fh.write('\n') if __name__ == "__main__": - pde_folder = pathlib.Path(__file__).parent.parent / 'pde/' - # remove sources folder if it already exists - if pde_folder.is_dir(): - shutil.rmtree(pde_folder) - pde_folder.mkdir(parents=True, exist_ok=True) + pde_folder = pathlib.Path(__file__).parent.parent / 'pde/' + # remove sources folder if it already exists + if pde_folder.is_dir(): + shutil.rmtree(pde_folder) + pde_folder.mkdir(parents=True, exist_ok=True) - contribs_text_file = pde_folder / 'contribs.txt' + contribs_text_file = pde_folder / 'contribs.txt' - contributions_list = preprocess_contributions() + contributions_list = preprocess_contributions() - # write contribs.txt file - with open(contribs_text_file, 'w+') as f: - write_contribs(contributions_list, f) + # write contribs.txt file + with open(contribs_text_file, 'w+') as f: + write_contribs(contributions_list, f) diff --git a/scripts/to_sources_jsons.py b/scripts/to_sources_jsons.py index 61d167d..4bd878c 100644 --- a/scripts/to_sources_jsons.py +++ b/scripts/to_sources_jsons.py @@ -28,39 +28,39 @@ def to_sources_dict(contribution_dict): - contribution_dict['props'] = contribution_dict.pop('source') - if contribution_dict['type'] == 'library': - sources_dict = { - field: contribution_dict[field] - for field in json_fields_library if field in contribution_dict - } - elif contribution_dict['type'] == 'examples': - sources_dict = { - field: contribution_dict[field] - for field in json_fields_example if field in contribution_dict - } - elif contribution_dict['type'] == 'tool': - sources_dict = { - field: contribution_dict[field] - for field in json_fields_tool if field in contribution_dict - } - else: - sources_dict = { - field: contribution_dict[field] - for field in json_fields_mode if field in contribution_dict - } + contribution_dict['props'] = contribution_dict.pop('source') + if contribution_dict['type'] == 'library': + sources_dict = { + field: contribution_dict[field] + for field in json_fields_library if field in contribution_dict + } + elif contribution_dict['type'] == 'examples': + sources_dict = { + field: contribution_dict[field] + for field in json_fields_example if field in contribution_dict + } + elif contribution_dict['type'] == 'tool': + sources_dict = { + field: contribution_dict[field] + for field in json_fields_tool if field in contribution_dict + } + else: + sources_dict = { + field: contribution_dict[field] + for field in json_fields_mode if field in contribution_dict + } - # put authors in list - sources_dict['authors'] = [sources_dict['authors']] if sources_dict['authors'] else sources_dict['authors'] + # put authors in list + sources_dict['authors'] = [sources_dict['authors']] if sources_dict['authors'] else sources_dict['authors'] - sources_dict['packages'] = [ - { - field:('java' if field == 'mode' else str(contribution_dict[field])) - for field in json_package_fields_list - } - ] + sources_dict['packages'] = [ + { + field:('java' if field == 'mode' else str(contribution_dict[field])) + for field in json_package_fields_list + } + ] - return sources_dict + return sources_dict def write_json_for_each_contribution_in_list(all_contributions, folder_path): @@ -75,14 +75,14 @@ def write_json_for_each_contribution_in_list(all_contributions, folder_path): if __name__ == "__main__": - sources_folder = pathlib.Path(__file__).parent.parent / 'sources/' + sources_folder = pathlib.Path(__file__).parent.parent / 'sources/' - contributions_list = get_valid_contributions() + contributions_list = get_valid_contributions() - # remove sources folder if it already exists - if sources_folder.is_dir(): - shutil.rmtree(sources_folder) - sources_folder.mkdir(parents=True, exist_ok=True) + # remove sources folder if it already exists + if sources_folder.is_dir(): + shutil.rmtree(sources_folder) + sources_folder.mkdir(parents=True, exist_ok=True) - # create a json file in the sources folder for each contribution - write_json_for_each_contribution_in_list(contributions_list, sources_folder) + # create a json file in the sources folder for each contribution + write_json_for_each_contribution_in_list(contributions_list, sources_folder) From 2b893c5858c7b4544abc942c7c061079093001e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=9Clgen=20Sar=C4=B1kavak?= Date: Wed, 12 Aug 2026 17:30:49 +0300 Subject: [PATCH 2/3] Add basic lint setup with ruff --- .github/workflows/git-hooks.yml | 29 +++++++++++++++++++++++++++++ .pre-commit-config.yaml | 7 +++++++ ruff.toml | 2 ++ 3 files changed, 38 insertions(+) create mode 100644 .github/workflows/git-hooks.yml create mode 100644 .pre-commit-config.yaml create mode 100644 ruff.toml diff --git a/.github/workflows/git-hooks.yml b/.github/workflows/git-hooks.yml new file mode 100644 index 0000000..3650d0c --- /dev/null +++ b/.github/workflows/git-hooks.yml @@ -0,0 +1,29 @@ +ame: "Git Hooks" + +on: + workflow_dispatch: + push: + branches: + - main + pull_request: ~ + +permissions: {} + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + run-prek: + name: prek run --all-files + runs-on: ubuntu-24.04 + timeout-minutes: 1 + + steps: + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Run prek + uses: j178/prek-action@4e14d07f9231acabce116ccfca13b13dd9755ece # v3.0.0 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..e0505e1 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,7 @@ +repos: + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.16.2 + hooks: + - id: ruff-check + args: ["--fix", "--show-fixes"] + - id: ruff-format diff --git a/ruff.toml b/ruff.toml new file mode 100644 index 0000000..570f049 --- /dev/null +++ b/ruff.toml @@ -0,0 +1,2 @@ +line-length = 120 +target-version = "py311" From bf5a9e9ede1ebf16dda3ef001b5de377fb137499 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=9Clgen=20Sar=C4=B1kavak?= Date: Wed, 12 Aug 2026 17:33:06 +0300 Subject: [PATCH 3/3] Apply ruff fixes --- scripts/add_new_contribution_to_yaml.py | 31 ++-- scripts/fetch_updates.py | 85 +++++----- scripts/parse_and_validate_properties_txt.py | 75 +++++---- scripts/to_contribs_txt.py | 80 +++++---- scripts/to_sources_jsons.py | 76 ++++----- scripts/utils.py | 17 +- tests/unit/conftest.py | 14 +- tests/unit/test_library_properties_new.py | 95 +++++------ tests/unit/test_properties_base.py | 90 +++++----- tests/unit/test_properties_existing.py | 91 +++++------ tests/unit/test_validate_and_model_dump.py | 163 +++++++++---------- 11 files changed, 383 insertions(+), 434 deletions(-) diff --git a/scripts/add_new_contribution_to_yaml.py b/scripts/add_new_contribution_to_yaml.py index 568313f..4a1923e 100644 --- a/scripts/add_new_contribution_to_yaml.py +++ b/scripts/add_new_contribution_to_yaml.py @@ -1,29 +1,30 @@ """ given properties, add a new contribution to the contributions.yaml database file. """ -from datetime import datetime, UTC + import json import pathlib +from datetime import UTC, datetime from sys import argv from ruamel.yaml import YAML def split_categories(categories): - categories = sorted(categories.replace('"', '').split(',')) + categories = sorted(categories.replace('"', "").split(",")) categories = [category.strip() for category in categories] return categories def postprocess_properties(properties_dict): - if 'categories' in properties_dict and properties_dict['categories']: - properties_dict['categories'] = split_categories(properties_dict['categories']) + if properties_dict.get("categories"): + properties_dict["categories"] = split_categories(properties_dict["categories"]) else: - properties_dict['categories'] = None + properties_dict["categories"] = None # add download - if 'download' not in properties_dict: - properties_dict['download'] = properties_dict['source'][:properties_dict['source'].rfind('.')] + '.zip' + if "download" not in properties_dict: + properties_dict["download"] = properties_dict["source"][: properties_dict["source"].rfind(".")] + ".zip" if __name__ == "__main__": @@ -35,29 +36,29 @@ def postprocess_properties(properties_dict): postprocess_properties(props) # open database - database_file = pathlib.Path(__file__).parent.parent / 'contributions.yaml' + database_file = pathlib.Path(__file__).parent.parent / "contributions.yaml" yaml = YAML() - with open(database_file, 'r') as db: + with open(database_file, "r") as db: data = yaml.load(db) - contributions_list = list(data['contributions']) + contributions_list = list(data["contributions"]) # find max index max_index = max([int(contribution["id"]) for contribution in contributions_list]) # append new contribution with next index # add status, at top - datetime_today = datetime.now(UTC).strftime('%Y-%m-%dT%H:%M:%S%z') + datetime_today = datetime.now(UTC).strftime("%Y-%m-%dT%H:%M:%S%z") contribution = { - 'id': max_index + 1, - 'status': 'VALID', - 'dateAdded': datetime_today, + "id": max_index + 1, + "status": "VALID", + "dateAdded": datetime_today, } contribution.update(props) contributions_list.append(contribution) # write all contributions to database file - with open(database_file, 'w') as db: + with open(database_file, "w") as db: yaml.dump({"contributions": contributions_list}, db) diff --git a/scripts/fetch_updates.py b/scripts/fetch_updates.py index f8bdf00..6e799e6 100644 --- a/scripts/fetch_updates.py +++ b/scripts/fetch_updates.py @@ -1,79 +1,82 @@ """ Reads in the contributions.yaml file, and updates the entries by hitting the 'source' url. """ + import argparse -from datetime import datetime, UTC import pathlib -from ruamel.yaml import YAML +from datetime import UTC, datetime from multiprocessing import Pool -from parse_and_validate_properties_txt import read_properties_txt, parse_text, validate_existing +from parse_and_validate_properties_txt import parse_text, read_properties_txt, validate_existing +from ruamel.yaml import YAML def update_contribution(contribution, props): - datetime_today = datetime.now(UTC).strftime('%Y-%m-%dT%H:%M:%S%z') - contribution['lastUpdated'] = datetime_today - if 'previousVersions' not in contribution: - contribution['previousVersions'] = [] - contribution['previousVersions'].append(contribution['prettyVersion']) + datetime_today = datetime.now(UTC).strftime("%Y-%m-%dT%H:%M:%S%z") + contribution["lastUpdated"] = datetime_today + if "previousVersions" not in contribution: + contribution["previousVersions"] = [] + contribution["previousVersions"].append(contribution["prettyVersion"]) # update from online - for field in props.keys(): + for field in props: # process category list - if field == 'categories': + if field == "categories": if props[field]: - contribution[field] = sorted(props[field].strip('"').split(',')) + contribution[field] = sorted(props[field].strip('"').split(",")) else: contribution[field] = [] else: contribution[field] = props[field] - if 'download' not in contribution: - contribution['download'] = contribution['source'][:contribution['source'].rfind('.')] + '.zip' + if "download" not in contribution: + contribution["download"] = contribution["source"][: contribution["source"].rfind(".")] + ".zip" - def log_broken(contribution, msg): - if contribution['status'] == 'VALID': - contribution['status'] = 'BROKEN' - if 'log' not in contribution: - contribution['log'] = [] - contribution['log'].append(msg) + if contribution["status"] == "VALID": + contribution["status"] = "BROKEN" + if "log" not in contribution: + contribution["log"] = [] + contribution["log"].append(msg) + def process_contribution(item): index, contribution = item - date_today = datetime.now(UTC).strftime('%Y-%m-%d') - this_version = '0' + date_today = datetime.now(UTC).strftime("%Y-%m-%d") + this_version = "0" - if contribution['status'] != 'DEPRECATED': + if contribution["status"] != "DEPRECATED": # compare version to what is at url. If has changed, update contribution to # what is online - if 'version' in contribution: - this_version = contribution['version'] + if "version" in contribution: + this_version = contribution["version"] try: - properties_raw = read_properties_txt(contribution['source']) + properties_raw = read_properties_txt(contribution["source"]) except FileNotFoundError as e: - log_broken(contribution, f'file not found, {e}, {date_today}') + log_broken(contribution, f"file not found, {e}, {date_today}") return index, contribution - except Exception: - log_broken(contribution, f'url timeout, {date_today}') + # TODO: Specify which exceptions are excepted and drop noqa: BLE001 + except Exception: # noqa: BLE001 + log_broken(contribution, f"url timeout, {date_today}") return index, contribution try: props = validate_existing(parse_text(properties_raw)) - except Exception: - log_broken(contribution, f'invalid file, {date_today}') + # TODO: Specify which exceptions are excepted and drop noqa: BLE001 + except Exception: # noqa: BLE001 + log_broken(contribution, f"invalid file, {date_today}") return index, contribution # some library files have field lastUpdated. This also exists in the database, but is defined # by our scripts, so remove this field. - contribution.pop('lastUpdated', None) + contribution.pop("lastUpdated", None) - contribution['status'] = 'VALID' + contribution["status"] = "VALID" - if props['version'] != this_version: + if props["version"] != this_version: # update from online update_contribution(contribution, props) return index, contribution @@ -93,33 +96,33 @@ def process_all(contributions_list): if __name__ == "__main__": parser = argparse.ArgumentParser() - parser.add_argument('--index') + parser.add_argument("--index") args = parser.parse_args() - index = 'all' + index = "all" if args.index: index = args.index - database_file = pathlib.Path(__file__).parent.parent / 'contributions.yaml' + database_file = pathlib.Path(__file__).parent.parent / "contributions.yaml" # read in database yaml file yaml = YAML() - with open(database_file, 'r') as db: + with open(database_file, "r") as db: data = yaml.load(db) - contributions_list = data['contributions'] + contributions_list = data["contributions"] - if index == 'all': + if index == "all": process_all(contributions_list) print("All processing complete") else: # update only contribution with id==index - contribution = next((x for x in contributions_list if x['id'] == int(index)), None) + contribution = next((x for x in contributions_list if x["id"] == int(index)), None) print(contribution) process_contribution((index, contribution)) print(contribution) # write all contributions to database file yaml = YAML() - with open(database_file, 'w') as outfile: + with open(database_file, "w") as outfile: yaml.dump({"contributions": contributions_list}, outfile) diff --git a/scripts/parse_and_validate_properties_txt.py b/scripts/parse_and_validate_properties_txt.py index 34a8aad..d141ceb 100644 --- a/scripts/parse_and_validate_properties_txt.py +++ b/scripts/parse_and_validate_properties_txt.py @@ -3,41 +3,40 @@ and validates the contents. If valid, it returns the contents as an object. """ -import json + import argparse +import json +import os +import javaproperties as jp import requests +from pydantic import AliasChoices, BaseModel, ConfigDict, Field, field_validator from tenacity import retry, stop_after_attempt, wait_fixed -import re -import os -from typing import Optional, Union -from pydantic import BaseModel, Field, ConfigDict, field_validator, AliasChoices -import javaproperties as jp - class PropertiesBase(BaseModel): name: str - authors: str = Field(validation_alias=AliasChoices('authors','authorList')) + authors: str = Field(validation_alias=AliasChoices("authors", "authorList")) url: str - categories: Optional[str] = Field(None, validation_alias=AliasChoices('categories','category')) + categories: str | None = Field(None, validation_alias=AliasChoices("categories", "category")) sentence: str - paragraph: Optional[str] = None + paragraph: str | None = None version: int prettyVersion: str minRevision: int = Field(0) maxRevision: int = Field(0) - modes: Optional[str] = Field(None, validation_alias=AliasChoices('modes','compatibleModesList')) + modes: str | None = Field(None, validation_alias=AliasChoices("modes", "compatibleModesList")) model_config = ConfigDict( - extra='allow', + extra="allow", ) + class PropertiesExisting(PropertiesBase): - version: Union[int, str] - prettyVersion: Optional[str] = None + version: int | str + prettyVersion: str | None = None - @field_validator('minRevision', 'maxRevision', mode='before') + @field_validator("minRevision", "maxRevision", mode="before") def default_on_error(cls, v): if v.isdigit(): return int(v) @@ -46,16 +45,14 @@ def default_on_error(cls, v): class LibraryPropertiesNew(PropertiesBase): - categories: str = Field(validation_alias=AliasChoices('categories','category')) + categories: str = Field(validation_alias=AliasChoices("categories", "category")) -@retry(stop=stop_after_attempt(3), - wait=wait_fixed(2), - reraise=True) +@retry(stop=stop_after_attempt(3), wait=wait_fixed(2), reraise=True) def read_properties_txt(properties_url): headers = { - 'User-Agent': 'Mozilla/5.0', - 'Accept': 'text/html', + "User-Agent": "Mozilla/5.0", + "Accept": "text/html", } r = requests.get(properties_url, headers=headers, timeout=30) @@ -64,62 +61,68 @@ def read_properties_txt(properties_url): return r.text + def parse_text(properties_raw): properties_dict = { - key: value.split('#')[0].strip() if isinstance(value, str) else value + key: value.split("#")[0].strip() if isinstance(value, str) else value for key, value in jp.loads(properties_raw).items() } return properties_dict + def validate_existing(properties_dict): # validation on existing contribution is weaker properties = PropertiesExisting.model_validate(properties_dict) return properties.model_dump() + def validate_new(properties_dict): # new contribution has stronger validation properties = PropertiesBase.model_validate(properties_dict) return properties.model_dump() + def validate_new_library(properties_dict): # new contribution has stronger validation properties = LibraryPropertiesNew.model_validate(properties_dict) return properties.model_dump() + def set_output(output_object): - with open(os.environ['GITHUB_OUTPUT'],'a') as f: - f.write(f'props={json.dumps(output_object)}') + with open(os.environ["GITHUB_OUTPUT"], "a") as f: + f.write(f"props={json.dumps(output_object)}") + def set_output_error(msg): - with open(os.environ['GITHUB_OUTPUT'],'a') as f: - f.write(f'error={msg}') + with open(os.environ["GITHUB_OUTPUT"], "a") as f: + f.write(f"error={msg}") if __name__ == "__main__": # this is used by github workflow, on new contributions. Use strong validation. # Add type to object parser = argparse.ArgumentParser() - parser.add_argument('type') - parser.add_argument('url') + parser.add_argument("type") + parser.add_argument("url") args = parser.parse_args() type_ = args.type url = args.url if not url.startswith("http"): print(f"Url not valid: {url}.\nStopping...") - set_output_error(f"Url is not valid. It should start with http:// or https://") + set_output_error("Url is not valid. It should start with http:// or https://") raise AssertionError print(f"url: {url}") # just for debugging, should do this via logging levels try: properties_raw = read_properties_txt(url) - except Exception as e: - set_output_error(f'Error when accessing url. Please ensure the url returns a valid properties text file') - raise e + except Exception: + set_output_error("Error when accessing url. Please ensure the url returns a valid properties text file") + raise print(f"properties text: {properties_raw}") # just for debugging, should do this via logging levels @@ -127,15 +130,15 @@ def set_output_error(msg): # this is because github actions will delimit strings with single quotes, and escapes single quotes this way properties_raw = properties_raw.replace("'", "''") try: - if type_ == 'library': + if type_ == "library": props = validate_new_library(parse_text(properties_raw)) else: props = validate_new(parse_text(properties_raw)) except Exception as e: - set_output_error(f'Errors when parsing file. Please check all required fields, and file format.\n\n{e}') - raise e + set_output_error(f"Errors when parsing file. Please check all required fields, and file format.\n\n{e}") + raise - contribution= { + contribution = { "type": type_, "source": url, } diff --git a/scripts/to_contribs_txt.py b/scripts/to_contribs_txt.py index 05be9f5..569d976 100644 --- a/scripts/to_contribs_txt.py +++ b/scripts/to_contribs_txt.py @@ -6,15 +6,27 @@ import pathlib import shutil from collections import defaultdict -from typing import List from utils import get_valid_contributions -type_list = ['library', 'examples', 'tool', 'mode'] +type_list = ["library", "examples", "tool", "mode"] contribs_fields_list = [ - 'name', 'authors', 'url', 'categories', 'sentence', 'paragraph', - 'version', 'prettyVersion', 'minRevision', 'maxRevision', 'imports', - 'modes', 'compatibleModesList', 'id', 'type', 'download' + "name", + "authors", + "url", + "categories", + "sentence", + "paragraph", + "version", + "prettyVersion", + "minRevision", + "maxRevision", + "imports", + "modes", + "compatibleModesList", + "id", + "type", + "download", ] @@ -24,15 +36,14 @@ def read_contribs_text(filepath): contrib_empty = True contrib_field_counts = defaultdict(int) - with open(filepath, 'r') as f: - for line in f.readlines(): - if line.strip() == "": - if not contrib_empty: - for key in list(this_contrib.keys()): - contrib_field_counts[key] += 1 - contribs_list.append(this_contrib) - this_contrib = {} - contrib_empty = True + with open(filepath, "r") as f: + for line in f: + if line.strip() == "" and not contrib_empty: + for key in list(this_contrib.keys()): + contrib_field_counts[key] += 1 + contribs_list.append(this_contrib) + this_contrib = {} + contrib_empty = True str_index = line.find("=") # capture first equals, if str_index >= 0: @@ -40,18 +51,19 @@ def read_contribs_text(filepath): this_contrib[field.strip()] = value.strip() contrib_empty = False - with open("contribs_txt_field_counts.json", 'w') as f: + with open("contribs_txt_field_counts.json", "w") as f: json.dump(contrib_field_counts, f) return contribs_list -def preprocess_contributions() -> List: +def preprocess_contributions() -> list: all_contributions = get_valid_contributions() # sort contributions list by type def sort_key(d): - return type_list.index(d['type']) + return type_list.index(d["type"]) + all_contributions = sorted(all_contributions, key=sort_key) return all_contributions @@ -59,40 +71,38 @@ def sort_key(d): def write_contribs(all_contributions, fh): for contribution in all_contributions: - fh.write(contribution['type'] + '\n') + fh.write(contribution["type"] + "\n") for field in contribs_fields_list: if field in contribution: - if field == 'id': - fh.write(f'{field}={contribution[field]:03}\n') - elif field == 'categories': - if contribution['type'] == 'library': - fh.write(f'{field}={",".join(contribution[field]) if contribution[field] else ""}\n') + if field == "id": + fh.write(f"{field}={contribution[field]:03}\n") + elif field == "categories": + if contribution["type"] == "library": + fh.write(f"{field}={','.join(contribution[field]) if contribution[field] else ''}\n") else: # categories are only relevant for libraries, except for examples with "Books" as category - if contribution[field] and 'Books' in contribution[field]: - fh.write(f'{field}={",".join(contribution[field]) if contribution[field] else ""}\n') + if contribution[field] and "Books" in contribution[field]: + fh.write(f"{field}={','.join(contribution[field]) if contribution[field] else ''}\n") else: - fh.write(f'{field}=\n') - elif field == 'compatibleModesList': - fh.write(f'modes={contribution[field]}\n') + fh.write(f"{field}=\n") + elif field == "compatibleModesList": + fh.write(f"modes={contribution[field]}\n") else: - fh.write(f'{field}={"" if contribution[field] is None else contribution[field]}\n') - fh.write('\n') + fh.write(f"{field}={'' if contribution[field] is None else contribution[field]}\n") + fh.write("\n") if __name__ == "__main__": - pde_folder = pathlib.Path(__file__).parent.parent / 'pde/' + pde_folder = pathlib.Path(__file__).parent.parent / "pde/" # remove sources folder if it already exists if pde_folder.is_dir(): shutil.rmtree(pde_folder) pde_folder.mkdir(parents=True, exist_ok=True) - contribs_text_file = pde_folder / 'contribs.txt' + contribs_text_file = pde_folder / "contribs.txt" contributions_list = preprocess_contributions() # write contribs.txt file - with open(contribs_text_file, 'w+') as f: + with open(contribs_text_file, "w+") as f: write_contribs(contributions_list, f) - - diff --git a/scripts/to_sources_jsons.py b/scripts/to_sources_jsons.py index 4bd878c..71fa656 100644 --- a/scripts/to_sources_jsons.py +++ b/scripts/to_sources_jsons.py @@ -8,56 +8,40 @@ from utils import get_valid_contributions -json_fields_library = [ - 'name', 'authors', 'url', 'categories', 'sentence', 'paragraph', 'imports', - 'id', 'type' -] +json_fields_library = ["name", "authors", "url", "categories", "sentence", "paragraph", "imports", "id", "type"] json_fields_example = [ - 'name', 'authors', 'url', 'categories', 'sentence', 'modes', 'paragraph', 'imports', - 'id', 'type' -] -json_fields_tool = [ - 'name', 'authors', 'url', 'categories', 'sentence', 'paragraph', 'imports', - 'id', 'type' -] -json_fields_mode = [ - 'name', 'authors', 'url', 'sentence', 'paragraph', 'imports', - 'id', 'type', 'categories' + "name", + "authors", + "url", + "categories", + "sentence", + "modes", + "paragraph", + "imports", + "id", + "type", ] -json_package_fields_list = ['mode', 'minRevision', 'maxRevision', 'props', 'download'] +json_fields_tool = ["name", "authors", "url", "categories", "sentence", "paragraph", "imports", "id", "type"] +json_fields_mode = ["name", "authors", "url", "sentence", "paragraph", "imports", "id", "type", "categories"] +json_package_fields_list = ["mode", "minRevision", "maxRevision", "props", "download"] def to_sources_dict(contribution_dict): - contribution_dict['props'] = contribution_dict.pop('source') - if contribution_dict['type'] == 'library': - sources_dict = { - field: contribution_dict[field] - for field in json_fields_library if field in contribution_dict - } - elif contribution_dict['type'] == 'examples': - sources_dict = { - field: contribution_dict[field] - for field in json_fields_example if field in contribution_dict - } - elif contribution_dict['type'] == 'tool': - sources_dict = { - field: contribution_dict[field] - for field in json_fields_tool if field in contribution_dict - } + contribution_dict["props"] = contribution_dict.pop("source") + if contribution_dict["type"] == "library": + sources_dict = {field: contribution_dict[field] for field in json_fields_library if field in contribution_dict} + elif contribution_dict["type"] == "examples": + sources_dict = {field: contribution_dict[field] for field in json_fields_example if field in contribution_dict} + elif contribution_dict["type"] == "tool": + sources_dict = {field: contribution_dict[field] for field in json_fields_tool if field in contribution_dict} else: - sources_dict = { - field: contribution_dict[field] - for field in json_fields_mode if field in contribution_dict - } + sources_dict = {field: contribution_dict[field] for field in json_fields_mode if field in contribution_dict} # put authors in list - sources_dict['authors'] = [sources_dict['authors']] if sources_dict['authors'] else sources_dict['authors'] + sources_dict["authors"] = [sources_dict["authors"]] if sources_dict["authors"] else sources_dict["authors"] - sources_dict['packages'] = [ - { - field:('java' if field == 'mode' else str(contribution_dict[field])) - for field in json_package_fields_list - } + sources_dict["packages"] = [ + {field: ("java" if field == "mode" else str(contribution_dict[field])) for field in json_package_fields_list} ] return sources_dict @@ -65,17 +49,17 @@ def to_sources_dict(contribution_dict): def write_json_for_each_contribution_in_list(all_contributions, folder_path): for contribution in all_contributions: - if 'name' in contribution: + if "name" in contribution: # output zero padded string for id - contribution['id'] = f"{contribution['id']:03}" - filename = contribution['name'].replace(':', '').replace('/', '').replace(' ', '_') + '.json' + contribution["id"] = f"{contribution['id']:03}" + filename = contribution["name"].replace(":", "").replace("/", "").replace(" ", "_") + ".json" this_filepath = folder_path / filename - with open(this_filepath, 'w') as f: + with open(this_filepath, "w") as f: json.dump(to_sources_dict(contribution), f, indent=2) if __name__ == "__main__": - sources_folder = pathlib.Path(__file__).parent.parent / 'sources/' + sources_folder = pathlib.Path(__file__).parent.parent / "sources/" contributions_list = get_valid_contributions() diff --git a/scripts/utils.py b/scripts/utils.py index ab9f55d..c9a7d14 100644 --- a/scripts/utils.py +++ b/scripts/utils.py @@ -1,20 +1,21 @@ import pathlib + from ruamel.yaml import YAML -database_file = pathlib.Path(__file__).parent.parent / 'contributions.yaml' +database_file = pathlib.Path(__file__).parent.parent / "contributions.yaml" def get_contributions(): # read in database yaml file yaml = YAML() - with open(database_file, 'r') as db: + with open(database_file, "r") as db: data = yaml.load(db) - contributions_list = data['contributions'] + contributions_list = data["contributions"] # filter contributions list, remove contribution status == BROKEN contributions_list = [ - contribution for contribution in contributions_list if contribution['status'] not in ["BROKEN", "DEPRECATED"] + contribution for contribution in contributions_list if contribution["status"] not in ["BROKEN", "DEPRECATED"] ] return contributions_list @@ -23,13 +24,13 @@ def get_contributions(): def apply_override(contributions_list): # apply override. if field additional_category, add value to categories for contribution in contributions_list: - if 'override' in contribution.keys(): - for key in contribution['override'].keys(): - contribution[key] = contribution['override'][key] + if "override" in contribution: + for key in contribution["override"]: + contribution[key] = contribution["override"][key] def get_valid_contributions(): contributions = get_contributions() apply_override(contributions) - return contributions \ No newline at end of file + return contributions diff --git a/tests/unit/conftest.py b/tests/unit/conftest.py index fe27af9..14b144e 100644 --- a/tests/unit/conftest.py +++ b/tests/unit/conftest.py @@ -16,7 +16,7 @@ def valid_properties_data(): "prettyVersion": "1.0.0", "minRevision": "5", "maxRevision": "10", - "modes": "standard,debug" + "modes": "standard,debug", } @@ -34,7 +34,7 @@ def valid_properties_data_aliases(): "prettyVersion": "1.0.0", "minRevision": "5", "maxRevision": "10", - "compatibleModesList": "standard,debug" + "compatibleModesList": "standard,debug", } @@ -47,7 +47,7 @@ def minimal_properties_base_data(): "url": "https://minimal.com", "sentence": "A minimal test case", "version": "1", - "prettyVersion": "1.0" + "prettyVersion": "1.0", } @@ -60,7 +60,7 @@ def minimal_properties_base_data_aliases(): "url": "https://minimal.com", "sentence": "A minimal test case", "version": "1", - "prettyVersion": "1.0" + "prettyVersion": "1.0", } @@ -98,7 +98,7 @@ def minimal_properties_library_data(): "categories": "minimal,library", "sentence": "A minimal test case", "version": "1", - "prettyVersion": "1.0" + "prettyVersion": "1.0", } @@ -112,7 +112,7 @@ def minimal_properties_library_data_aliases(): "category": "minimal,library", "sentence": "A minimal test case", "version": "1", - "prettyVersion": "1.0" + "prettyVersion": "1.0", } @@ -128,5 +128,5 @@ def properties_with_extra_fields(): "version": "2", "prettyVersion": "2.0", "customField": "custom value", - "anotherExtra": "42" + "anotherExtra": "42", } diff --git a/tests/unit/test_library_properties_new.py b/tests/unit/test_library_properties_new.py index 0483321..43dea36 100644 --- a/tests/unit/test_library_properties_new.py +++ b/tests/unit/test_library_properties_new.py @@ -1,87 +1,82 @@ import pytest from pydantic import ValidationError + from scripts.parse_and_validate_properties_txt import LibraryPropertiesNew # Test Cases class TestLibraryPropertiesNew: - def test_valid_complete_data(self, valid_properties_data): """Test creation with all valid fields""" props = LibraryPropertiesNew(**valid_properties_data) - assert props.name == valid_properties_data['name'] - assert props.authors == valid_properties_data['authors'] - assert props.url == valid_properties_data['url'] - assert props.categories == valid_properties_data['categories'] - assert props.sentence == valid_properties_data['sentence'] - assert props.paragraph == valid_properties_data['paragraph'] - assert props.version == int(valid_properties_data['version']) - assert props.prettyVersion == valid_properties_data['prettyVersion'] - assert props.minRevision == int(valid_properties_data['minRevision']) - assert props.maxRevision == int(valid_properties_data['maxRevision']) - assert props.modes == valid_properties_data['modes'] - + assert props.name == valid_properties_data["name"] + assert props.authors == valid_properties_data["authors"] + assert props.url == valid_properties_data["url"] + assert props.categories == valid_properties_data["categories"] + assert props.sentence == valid_properties_data["sentence"] + assert props.paragraph == valid_properties_data["paragraph"] + assert props.version == int(valid_properties_data["version"]) + assert props.prettyVersion == valid_properties_data["prettyVersion"] + assert props.minRevision == int(valid_properties_data["minRevision"]) + assert props.maxRevision == int(valid_properties_data["maxRevision"]) + assert props.modes == valid_properties_data["modes"] def test_valid_complete_data_aliases(self, valid_properties_data_aliases): """Test creation with all valid fields""" props = LibraryPropertiesNew(**valid_properties_data_aliases) - assert props.name == valid_properties_data_aliases['name'] - assert props.authors == valid_properties_data_aliases['authorList'] - assert props.url == valid_properties_data_aliases['url'] - assert props.categories == valid_properties_data_aliases['category'] - assert props.sentence == valid_properties_data_aliases['sentence'] - assert props.paragraph == valid_properties_data_aliases['paragraph'] - assert props.version == int(valid_properties_data_aliases['version']) - assert props.prettyVersion == valid_properties_data_aliases['prettyVersion'] - assert props.minRevision == int(valid_properties_data_aliases['minRevision']) - assert props.maxRevision == int(valid_properties_data_aliases['maxRevision']) - assert props.modes == valid_properties_data_aliases['compatibleModesList'] - + assert props.name == valid_properties_data_aliases["name"] + assert props.authors == valid_properties_data_aliases["authorList"] + assert props.url == valid_properties_data_aliases["url"] + assert props.categories == valid_properties_data_aliases["category"] + assert props.sentence == valid_properties_data_aliases["sentence"] + assert props.paragraph == valid_properties_data_aliases["paragraph"] + assert props.version == int(valid_properties_data_aliases["version"]) + assert props.prettyVersion == valid_properties_data_aliases["prettyVersion"] + assert props.minRevision == int(valid_properties_data_aliases["minRevision"]) + assert props.maxRevision == int(valid_properties_data_aliases["maxRevision"]) + assert props.modes == valid_properties_data_aliases["compatibleModesList"] def test_minimal_required_data(self, minimal_properties_library_data): """Test creation with only required fields""" props = LibraryPropertiesNew(**minimal_properties_library_data) - assert props.name == minimal_properties_library_data['name'] - assert props.authors == minimal_properties_library_data['authors'] - assert props.url == minimal_properties_library_data['url'] - assert props.categories == minimal_properties_library_data['categories'] - assert props.sentence == minimal_properties_library_data['sentence'] + assert props.name == minimal_properties_library_data["name"] + assert props.authors == minimal_properties_library_data["authors"] + assert props.url == minimal_properties_library_data["url"] + assert props.categories == minimal_properties_library_data["categories"] + assert props.sentence == minimal_properties_library_data["sentence"] assert props.paragraph is None - assert props.version == int(minimal_properties_library_data['version']) - assert props.prettyVersion == minimal_properties_library_data['prettyVersion'] + assert props.version == int(minimal_properties_library_data["version"]) + assert props.prettyVersion == minimal_properties_library_data["prettyVersion"] assert props.minRevision == 0 # Default value assert props.maxRevision == 0 # Default value assert props.modes is None - def test_minimal_required_data_aliases(self, minimal_properties_library_data_aliases): """Test creation with only required fields""" props = LibraryPropertiesNew(**minimal_properties_library_data_aliases) - assert props.name == minimal_properties_library_data_aliases['name'] - assert props.authors == minimal_properties_library_data_aliases['authorList'] - assert props.url == minimal_properties_library_data_aliases['url'] - assert props.categories == minimal_properties_library_data_aliases['category'] - assert props.sentence == minimal_properties_library_data_aliases['sentence'] + assert props.name == minimal_properties_library_data_aliases["name"] + assert props.authors == minimal_properties_library_data_aliases["authorList"] + assert props.url == minimal_properties_library_data_aliases["url"] + assert props.categories == minimal_properties_library_data_aliases["category"] + assert props.sentence == minimal_properties_library_data_aliases["sentence"] assert props.paragraph is None - assert props.version == int(minimal_properties_library_data_aliases['version']) - assert props.prettyVersion == minimal_properties_library_data_aliases['prettyVersion'] + assert props.version == int(minimal_properties_library_data_aliases["version"]) + assert props.prettyVersion == minimal_properties_library_data_aliases["prettyVersion"] assert props.minRevision == 0 # Default value assert props.maxRevision == 0 # Default value assert props.modes is None - def test_extra_fields_allowed(self, properties_with_extra_fields): """Test that extra fields are allowed due to extra='allow'""" props = LibraryPropertiesNew(**properties_with_extra_fields) - assert props.name == properties_with_extra_fields['name'] - assert props.customField == properties_with_extra_fields['customField'] - assert props.anotherExtra == properties_with_extra_fields['anotherExtra'] - + assert props.name == properties_with_extra_fields["name"] + assert props.customField == properties_with_extra_fields["customField"] + assert props.anotherExtra == properties_with_extra_fields["anotherExtra"] def test_missing_required_field_name(self, minimal_properties_library_data): """Test validation error when required field 'name' is missing""" @@ -92,7 +87,6 @@ def test_missing_required_field_name(self, minimal_properties_library_data): assert "name" in str(exc_info.value) assert "Field required" in str(exc_info.value) - def test_missing_required_field_authors(self, minimal_properties_library_data): """Test validation error when required field 'authors' is missing""" minimal_properties_library_data.pop("authors") @@ -101,7 +95,6 @@ def test_missing_required_field_authors(self, minimal_properties_library_data): assert "authors" in str(exc_info.value) - def test_missing_required_field_url(self, minimal_properties_library_data): """Test validation error when required field 'url' is missing""" minimal_properties_library_data.pop("url") @@ -110,7 +103,6 @@ def test_missing_required_field_url(self, minimal_properties_library_data): assert "url" in str(exc_info.value) - def test_missing_required_field_categories(self, minimal_properties_library_data): """Test validation error when required field 'categories' is missing""" minimal_properties_library_data.pop("categories") @@ -119,7 +111,6 @@ def test_missing_required_field_categories(self, minimal_properties_library_data assert "categories" in str(exc_info.value) - def test_missing_required_field_sentence(self, minimal_properties_library_data): """Test validation error when required field 'sentence' is missing""" minimal_properties_library_data.pop("sentence") @@ -128,7 +119,6 @@ def test_missing_required_field_sentence(self, minimal_properties_library_data): assert "sentence" in str(exc_info.value) - def test_missing_required_field_version(self, minimal_properties_library_data): """Test validation error when required field 'version' is missing""" minimal_properties_library_data.pop("version") @@ -137,7 +127,6 @@ def test_missing_required_field_version(self, minimal_properties_library_data): assert "version" in str(exc_info.value) - def test_missing_required_field_pretty_version(self, minimal_properties_library_data): """Test validation error when required field 'prettyVersion' is missing""" minimal_properties_library_data.pop("prettyVersion") @@ -146,7 +135,6 @@ def test_missing_required_field_pretty_version(self, minimal_properties_library_ assert "prettyVersion" in str(exc_info.value) - def test_invalid_type_version(self, minimal_properties_library_data): """Test validation error when 'version' has wrong type""" minimal_properties_library_data["version"] = "not_an_int" @@ -156,7 +144,6 @@ def test_invalid_type_version(self, minimal_properties_library_data): assert "version" in str(exc_info.value) assert "Input should be a valid integer" in str(exc_info.value) - def test_invalid_type_min_revision(self, minimal_properties_library_data): """Test validation error when 'minRevision' has wrong type""" minimal_properties_library_data["minRevision"] = "not_an_int" @@ -166,7 +153,6 @@ def test_invalid_type_min_revision(self, minimal_properties_library_data): assert "minRevision" in str(exc_info.value) assert "Input should be a valid integer" in str(exc_info.value) - def test_invalid_type_max_revision(self, minimal_properties_library_data): """Test validation error when 'maxRevision' has wrong type""" minimal_properties_library_data["maxRevision"] = "not_an_int" @@ -176,7 +162,6 @@ def test_invalid_type_max_revision(self, minimal_properties_library_data): assert "maxRevision" in str(exc_info.value) assert "Input should be a valid integer" in str(exc_info.value) - def test_model_dump(self, valid_properties_data): """Test that model serialization works correctly""" props = LibraryPropertiesNew(**valid_properties_data) diff --git a/tests/unit/test_properties_base.py b/tests/unit/test_properties_base.py index 8e8495a..17e9bbf 100644 --- a/tests/unit/test_properties_base.py +++ b/tests/unit/test_properties_base.py @@ -1,87 +1,82 @@ import pytest from pydantic import ValidationError + from scripts.parse_and_validate_properties_txt import PropertiesBase # Test Cases class TestPropertiesBase: - def test_valid_complete_data(self, valid_properties_data): """Test creation with all valid fields""" props = PropertiesBase(**valid_properties_data) - assert props.name == valid_properties_data['name'] - assert props.authors == valid_properties_data['authors'] - assert props.url == valid_properties_data['url'] - assert props.categories == valid_properties_data['categories'] - assert props.sentence == valid_properties_data['sentence'] - assert props.paragraph == valid_properties_data['paragraph'] - assert props.version == int(valid_properties_data['version']) - assert props.prettyVersion == valid_properties_data['prettyVersion'] - assert props.minRevision == int(valid_properties_data['minRevision']) - assert props.maxRevision == int(valid_properties_data['maxRevision']) - assert props.modes == valid_properties_data['modes'] - + assert props.name == valid_properties_data["name"] + assert props.authors == valid_properties_data["authors"] + assert props.url == valid_properties_data["url"] + assert props.categories == valid_properties_data["categories"] + assert props.sentence == valid_properties_data["sentence"] + assert props.paragraph == valid_properties_data["paragraph"] + assert props.version == int(valid_properties_data["version"]) + assert props.prettyVersion == valid_properties_data["prettyVersion"] + assert props.minRevision == int(valid_properties_data["minRevision"]) + assert props.maxRevision == int(valid_properties_data["maxRevision"]) + assert props.modes == valid_properties_data["modes"] def test_valid_complete_data_aliases(self, valid_properties_data_aliases): """Test creation with all valid fields""" props = PropertiesBase(**valid_properties_data_aliases) - assert props.name == valid_properties_data_aliases['name'] - assert props.authors == valid_properties_data_aliases['authorList'] - assert props.url == valid_properties_data_aliases['url'] - assert props.categories == valid_properties_data_aliases['category'] - assert props.sentence == valid_properties_data_aliases['sentence'] - assert props.paragraph == valid_properties_data_aliases['paragraph'] - assert props.version == int(valid_properties_data_aliases['version']) - assert props.prettyVersion == valid_properties_data_aliases['prettyVersion'] - assert props.minRevision == int(valid_properties_data_aliases['minRevision']) - assert props.maxRevision == int(valid_properties_data_aliases['maxRevision']) - assert props.modes == valid_properties_data_aliases['compatibleModesList'] - + assert props.name == valid_properties_data_aliases["name"] + assert props.authors == valid_properties_data_aliases["authorList"] + assert props.url == valid_properties_data_aliases["url"] + assert props.categories == valid_properties_data_aliases["category"] + assert props.sentence == valid_properties_data_aliases["sentence"] + assert props.paragraph == valid_properties_data_aliases["paragraph"] + assert props.version == int(valid_properties_data_aliases["version"]) + assert props.prettyVersion == valid_properties_data_aliases["prettyVersion"] + assert props.minRevision == int(valid_properties_data_aliases["minRevision"]) + assert props.maxRevision == int(valid_properties_data_aliases["maxRevision"]) + assert props.modes == valid_properties_data_aliases["compatibleModesList"] def test_minimal_required_data(self, minimal_properties_base_data): """Test creation with only required fields""" props = PropertiesBase(**minimal_properties_base_data) - assert props.name == minimal_properties_base_data['name'] - assert props.authors == minimal_properties_base_data['authors'] - assert props.url == minimal_properties_base_data['url'] + assert props.name == minimal_properties_base_data["name"] + assert props.authors == minimal_properties_base_data["authors"] + assert props.url == minimal_properties_base_data["url"] assert props.categories is None - assert props.sentence == minimal_properties_base_data['sentence'] + assert props.sentence == minimal_properties_base_data["sentence"] assert props.paragraph is None - assert props.version == int(minimal_properties_base_data['version']) - assert props.prettyVersion == minimal_properties_base_data['prettyVersion'] + assert props.version == int(minimal_properties_base_data["version"]) + assert props.prettyVersion == minimal_properties_base_data["prettyVersion"] assert props.minRevision == 0 # Default value assert props.maxRevision == 0 # Default value assert props.modes is None - def test_minimal_required_data_aliases(self, minimal_properties_base_data_aliases): """Test creation with only required fields""" props = PropertiesBase(**minimal_properties_base_data_aliases) - assert props.name == minimal_properties_base_data_aliases['name'] - assert props.authors == minimal_properties_base_data_aliases['authorList'] - assert props.url == minimal_properties_base_data_aliases['url'] + assert props.name == minimal_properties_base_data_aliases["name"] + assert props.authors == minimal_properties_base_data_aliases["authorList"] + assert props.url == minimal_properties_base_data_aliases["url"] assert props.categories is None - assert props.sentence == minimal_properties_base_data_aliases['sentence'] + assert props.sentence == minimal_properties_base_data_aliases["sentence"] assert props.paragraph is None - assert props.version == int(minimal_properties_base_data_aliases['version']) - assert props.prettyVersion == minimal_properties_base_data_aliases['prettyVersion'] + assert props.version == int(minimal_properties_base_data_aliases["version"]) + assert props.prettyVersion == minimal_properties_base_data_aliases["prettyVersion"] assert props.minRevision == 0 # Default value assert props.maxRevision == 0 # Default value assert props.modes is None - def test_extra_fields_allowed(self, properties_with_extra_fields): """Test that extra fields are allowed due to extra='allow'""" props = PropertiesBase(**properties_with_extra_fields) - assert props.name == properties_with_extra_fields['name'] - assert props.customField == properties_with_extra_fields['customField'] - assert props.anotherExtra == properties_with_extra_fields['anotherExtra'] - + assert props.name == properties_with_extra_fields["name"] + assert props.customField == properties_with_extra_fields["customField"] + assert props.anotherExtra == properties_with_extra_fields["anotherExtra"] def test_missing_required_field_name(self, minimal_properties_base_data): """Test validation error when required field 'name' is missing""" @@ -93,7 +88,6 @@ def test_missing_required_field_name(self, minimal_properties_base_data): assert "name" in str(exc_info.value) assert "Field required" in str(exc_info.value) - def test_missing_required_field_authors(self, minimal_properties_base_data): """Test validation error when required field 'authors' is missing""" minimal_properties_base_data.pop("authors") @@ -103,7 +97,6 @@ def test_missing_required_field_authors(self, minimal_properties_base_data): assert "authors" in str(exc_info.value) - def test_missing_required_field_url(self, minimal_properties_base_data): """Test validation error when required field 'url' is missing""" minimal_properties_base_data.pop("url") @@ -113,7 +106,6 @@ def test_missing_required_field_url(self, minimal_properties_base_data): assert "url" in str(exc_info.value) - def test_missing_required_field_sentence(self, minimal_properties_base_data): """Test validation error when required field 'sentence' is missing""" minimal_properties_base_data.pop("sentence") @@ -123,7 +115,6 @@ def test_missing_required_field_sentence(self, minimal_properties_base_data): assert "sentence" in str(exc_info.value) - def test_missing_required_field_version(self, minimal_properties_base_data): """Test validation error when required field 'version' is missing""" minimal_properties_base_data.pop("version") @@ -133,7 +124,6 @@ def test_missing_required_field_version(self, minimal_properties_base_data): assert "version" in str(exc_info.value) - def test_missing_required_field_pretty_version(self, minimal_properties_base_data): """Test validation error when required field 'prettyVersion' is missing""" minimal_properties_base_data.pop("prettyVersion") @@ -143,7 +133,6 @@ def test_missing_required_field_pretty_version(self, minimal_properties_base_dat assert "prettyVersion" in str(exc_info.value) - def test_invalid_type_version(self, minimal_properties_base_data): """Test validation error when 'version' has wrong type""" minimal_properties_base_data["version"] = "not_an_int" @@ -153,7 +142,6 @@ def test_invalid_type_version(self, minimal_properties_base_data): assert "version" in str(exc_info.value) assert "Input should be a valid integer" in str(exc_info.value) - def test_invalid_type_min_revision(self, minimal_properties_base_data): """Test validation error when 'minRevision' has wrong type""" minimal_properties_base_data["minRevision"] = "not_an_int" @@ -163,7 +151,6 @@ def test_invalid_type_min_revision(self, minimal_properties_base_data): assert "minRevision" in str(exc_info.value) assert "Input should be a valid integer" in str(exc_info.value) - def test_invalid_type_max_revision(self, minimal_properties_base_data): """Test validation error when 'maxRevision' has wrong type""" minimal_properties_base_data["maxRevision"] = "not_an_int" @@ -173,7 +160,6 @@ def test_invalid_type_max_revision(self, minimal_properties_base_data): assert "maxRevision" in str(exc_info.value) assert "Input should be a valid integer" in str(exc_info.value) - def test_model_dump(self, valid_properties_data): """Test that model serialization works correctly""" props = PropertiesBase(**valid_properties_data) diff --git a/tests/unit/test_properties_existing.py b/tests/unit/test_properties_existing.py index 1c05bf0..a85da60 100644 --- a/tests/unit/test_properties_existing.py +++ b/tests/unit/test_properties_existing.py @@ -1,5 +1,6 @@ import pytest from pydantic import ValidationError + from scripts.parse_and_validate_properties_txt import PropertiesExisting @@ -9,78 +10,73 @@ def test_valid_complete_data(self, valid_properties_data): """Test creation with all valid fields""" props = PropertiesExisting(**valid_properties_data) - assert props.name == valid_properties_data['name'] - assert props.authors == valid_properties_data['authors'] - assert props.url == valid_properties_data['url'] - assert props.categories == valid_properties_data['categories'] - assert props.sentence == valid_properties_data['sentence'] - assert props.paragraph == valid_properties_data['paragraph'] - assert props.version == valid_properties_data['version'] - assert props.prettyVersion == valid_properties_data['prettyVersion'] - assert props.minRevision == int(valid_properties_data['minRevision']) - assert props.maxRevision == int(valid_properties_data['maxRevision']) - assert props.modes == valid_properties_data['modes'] - + assert props.name == valid_properties_data["name"] + assert props.authors == valid_properties_data["authors"] + assert props.url == valid_properties_data["url"] + assert props.categories == valid_properties_data["categories"] + assert props.sentence == valid_properties_data["sentence"] + assert props.paragraph == valid_properties_data["paragraph"] + assert props.version == valid_properties_data["version"] + assert props.prettyVersion == valid_properties_data["prettyVersion"] + assert props.minRevision == int(valid_properties_data["minRevision"]) + assert props.maxRevision == int(valid_properties_data["maxRevision"]) + assert props.modes == valid_properties_data["modes"] def test_valid_complete_data_aliases(self, valid_properties_data_aliases): """Test creation with all valid fields""" props = PropertiesExisting(**valid_properties_data_aliases) - assert props.name == valid_properties_data_aliases['name'] - assert props.authors == valid_properties_data_aliases['authorList'] - assert props.url == valid_properties_data_aliases['url'] - assert props.categories == valid_properties_data_aliases['category'] - assert props.sentence == valid_properties_data_aliases['sentence'] - assert props.paragraph == valid_properties_data_aliases['paragraph'] - assert props.version == valid_properties_data_aliases['version'] - assert props.prettyVersion == valid_properties_data_aliases['prettyVersion'] - assert props.minRevision == int(valid_properties_data_aliases['minRevision']) - assert props.maxRevision == int(valid_properties_data_aliases['maxRevision']) - assert props.modes == valid_properties_data_aliases['compatibleModesList'] - + assert props.name == valid_properties_data_aliases["name"] + assert props.authors == valid_properties_data_aliases["authorList"] + assert props.url == valid_properties_data_aliases["url"] + assert props.categories == valid_properties_data_aliases["category"] + assert props.sentence == valid_properties_data_aliases["sentence"] + assert props.paragraph == valid_properties_data_aliases["paragraph"] + assert props.version == valid_properties_data_aliases["version"] + assert props.prettyVersion == valid_properties_data_aliases["prettyVersion"] + assert props.minRevision == int(valid_properties_data_aliases["minRevision"]) + assert props.maxRevision == int(valid_properties_data_aliases["maxRevision"]) + assert props.modes == valid_properties_data_aliases["compatibleModesList"] def test_minimal_required_data(self, minimal_properties_existing_data): """Test creation with only required fields""" props = PropertiesExisting(**minimal_properties_existing_data) - assert props.name == minimal_properties_existing_data['name'] - assert props.authors == minimal_properties_existing_data['authors'] - assert props.url == minimal_properties_existing_data['url'] + assert props.name == minimal_properties_existing_data["name"] + assert props.authors == minimal_properties_existing_data["authors"] + assert props.url == minimal_properties_existing_data["url"] assert props.categories is None - assert props.sentence == minimal_properties_existing_data['sentence'] + assert props.sentence == minimal_properties_existing_data["sentence"] assert props.paragraph is None - assert props.version == minimal_properties_existing_data['version'] + assert props.version == minimal_properties_existing_data["version"] assert props.prettyVersion is None assert props.minRevision == 0 # Default value assert props.maxRevision == 0 # Default value assert props.modes is None - def test_minimal_required_data_alias(self, minimal_properties_existing_data_aliases): """Test creation with only required fields""" props = PropertiesExisting(**minimal_properties_existing_data_aliases) - assert props.name == minimal_properties_existing_data_aliases['name'] - assert props.authors == minimal_properties_existing_data_aliases['authorList'] - assert props.url == minimal_properties_existing_data_aliases['url'] + assert props.name == minimal_properties_existing_data_aliases["name"] + assert props.authors == minimal_properties_existing_data_aliases["authorList"] + assert props.url == minimal_properties_existing_data_aliases["url"] assert props.categories is None - assert props.sentence == minimal_properties_existing_data_aliases['sentence'] + assert props.sentence == minimal_properties_existing_data_aliases["sentence"] assert props.paragraph is None - assert props.version == minimal_properties_existing_data_aliases['version'] + assert props.version == minimal_properties_existing_data_aliases["version"] assert props.prettyVersion is None assert props.minRevision == 0 # Default value assert props.maxRevision == 0 # Default value assert props.modes is None - def test_extra_fields_allowed(self, properties_with_extra_fields): """Test that extra fields are allowed due to extra='allow'""" props = PropertiesExisting(**properties_with_extra_fields) - assert props.name == properties_with_extra_fields['name'] - assert props.customField == properties_with_extra_fields['customField'] - assert props.anotherExtra == properties_with_extra_fields['anotherExtra'] - + assert props.name == properties_with_extra_fields["name"] + assert props.customField == properties_with_extra_fields["customField"] + assert props.anotherExtra == properties_with_extra_fields["anotherExtra"] def test_missing_required_field_name(self, minimal_properties_existing_data): """Test validation error when required field 'name' is missing""" @@ -92,7 +88,6 @@ def test_missing_required_field_name(self, minimal_properties_existing_data): assert "name" in str(exc_info.value) assert "Field required" in str(exc_info.value) - def test_missing_required_field_authors(self, minimal_properties_existing_data): """Test validation error when required field 'authors' is missing""" minimal_properties_existing_data.pop("authors") @@ -102,7 +97,6 @@ def test_missing_required_field_authors(self, minimal_properties_existing_data): assert "authors" in str(exc_info.value) - def test_missing_required_field_url(self, minimal_properties_existing_data): """Test validation error when required field 'url' is missing""" minimal_properties_existing_data.pop("url") @@ -112,7 +106,6 @@ def test_missing_required_field_url(self, minimal_properties_existing_data): assert "url" in str(exc_info.value) - def test_missing_required_field_sentence(self, minimal_properties_existing_data): """Test validation error when required field 'sentence' is missing""" minimal_properties_existing_data.pop("sentence") @@ -122,7 +115,6 @@ def test_missing_required_field_sentence(self, minimal_properties_existing_data) assert "sentence" in str(exc_info.value) - def test_missing_required_field_version(self, minimal_properties_existing_data): """Test validation error when required field 'version' is missing""" minimal_properties_existing_data.pop("version") @@ -132,28 +124,24 @@ def test_missing_required_field_version(self, minimal_properties_existing_data): assert "version" in str(exc_info.value) - def test_string_type_version(self, minimal_properties_existing_data): """Test validation when 'version' is string""" minimal_properties_existing_data["version"] = "not_an_int" props = PropertiesExisting(**minimal_properties_existing_data) - assert props.version == minimal_properties_existing_data['version'] - + assert props.version == minimal_properties_existing_data["version"] def test_string_type_min_revision(self, minimal_properties_existing_data): """Test when 'minRevision' string defaults to 0""" minimal_properties_existing_data["minRevision"] = "not_an_int" - props =PropertiesExisting(**minimal_properties_existing_data) + props = PropertiesExisting(**minimal_properties_existing_data) assert props.minRevision == 0 - def test_string_type_max_revision(self, minimal_properties_existing_data): """Test when 'maxRevision' string defaults to 0""" minimal_properties_existing_data["maxRevision"] = "not_an_int" - props =PropertiesExisting(**minimal_properties_existing_data) + props = PropertiesExisting(**minimal_properties_existing_data) assert props.maxRevision == 0 - def test_model_dump(self, valid_properties_data): """Test that model serialization works correctly""" props = PropertiesExisting(**valid_properties_data) @@ -164,6 +152,5 @@ def test_model_dump(self, valid_properties_data): assert "compatibleModesList" not in dumped # Should use field name, not alias - if __name__ == "__main__": pytest.main([__file__, "-v"]) diff --git a/tests/unit/test_validate_and_model_dump.py b/tests/unit/test_validate_and_model_dump.py index 2e36564..a8ed7e9 100644 --- a/tests/unit/test_validate_and_model_dump.py +++ b/tests/unit/test_validate_and_model_dump.py @@ -1,135 +1,124 @@ -import pytest -from pydantic import ValidationError -from scripts.parse_and_validate_properties_txt import validate_new, validate_existing, validate_new_library +from scripts.parse_and_validate_properties_txt import validate_existing, validate_new, validate_new_library # Test Cases class TestValidateAndExport: - def test_validate_existing_complete_data(self, valid_properties_data): """Test validate_existing with complete data""" props = validate_existing(valid_properties_data) - assert props['name'] == valid_properties_data['name'] - assert props['authors'] == valid_properties_data['authors'] - assert props['url'] == valid_properties_data['url'] - assert props['categories'] == valid_properties_data['categories'] - assert props['sentence'] == valid_properties_data['sentence'] - assert props['paragraph'] == valid_properties_data['paragraph'] - assert props['version'] == valid_properties_data['version'] - assert props['prettyVersion'] == valid_properties_data['prettyVersion'] - assert props['minRevision'] == int(valid_properties_data['minRevision']) - assert props['maxRevision'] == int(valid_properties_data['maxRevision']) - assert props['modes'] == valid_properties_data['modes'] - + assert props["name"] == valid_properties_data["name"] + assert props["authors"] == valid_properties_data["authors"] + assert props["url"] == valid_properties_data["url"] + assert props["categories"] == valid_properties_data["categories"] + assert props["sentence"] == valid_properties_data["sentence"] + assert props["paragraph"] == valid_properties_data["paragraph"] + assert props["version"] == valid_properties_data["version"] + assert props["prettyVersion"] == valid_properties_data["prettyVersion"] + assert props["minRevision"] == int(valid_properties_data["minRevision"]) + assert props["maxRevision"] == int(valid_properties_data["maxRevision"]) + assert props["modes"] == valid_properties_data["modes"] def test_validate_existing_minimal_required_data(self, minimal_properties_existing_data): """Test validate_existing with minimal data""" props = validate_existing(minimal_properties_existing_data) - assert props['name'] == minimal_properties_existing_data['name'] - assert props['authors'] == minimal_properties_existing_data['authors'] - assert props['url'] == minimal_properties_existing_data['url'] - assert props['categories'] is None - assert props['sentence'] == minimal_properties_existing_data['sentence'] - assert props['paragraph'] is None - assert props['version'] == minimal_properties_existing_data['version'] - assert props['prettyVersion'] is None - assert props['minRevision'] == 0 # Default value - assert props['maxRevision'] == 0 # Default value - assert props['modes'] is None - + assert props["name"] == minimal_properties_existing_data["name"] + assert props["authors"] == minimal_properties_existing_data["authors"] + assert props["url"] == minimal_properties_existing_data["url"] + assert props["categories"] is None + assert props["sentence"] == minimal_properties_existing_data["sentence"] + assert props["paragraph"] is None + assert props["version"] == minimal_properties_existing_data["version"] + assert props["prettyVersion"] is None + assert props["minRevision"] == 0 # Default value + assert props["maxRevision"] == 0 # Default value + assert props["modes"] is None def test_validate_existing_extra_fields_allowed(self, properties_with_extra_fields): """Test validate_existing with extra fields""" props = validate_existing(properties_with_extra_fields) - assert props['name'] == properties_with_extra_fields['name'] - assert props['customField'] == properties_with_extra_fields['customField'] - assert props['anotherExtra'] == properties_with_extra_fields['anotherExtra'] - + assert props["name"] == properties_with_extra_fields["name"] + assert props["customField"] == properties_with_extra_fields["customField"] + assert props["anotherExtra"] == properties_with_extra_fields["anotherExtra"] def test_validate_new_complete_data(self, valid_properties_data): """Test validate_new with complete data""" props = validate_new(valid_properties_data) - assert props['name'] == valid_properties_data['name'] - assert props['authors'] == valid_properties_data['authors'] - assert props['url'] == valid_properties_data['url'] - assert props['categories'] == valid_properties_data['categories'] - assert props['sentence'] == valid_properties_data['sentence'] - assert props['paragraph'] == valid_properties_data['paragraph'] - assert props['version'] == int(valid_properties_data['version']) - assert props['prettyVersion'] == valid_properties_data['prettyVersion'] - assert props['minRevision'] == int(valid_properties_data['minRevision']) - assert props['maxRevision'] == int(valid_properties_data['maxRevision']) - assert props['modes'] == valid_properties_data['modes'] - + assert props["name"] == valid_properties_data["name"] + assert props["authors"] == valid_properties_data["authors"] + assert props["url"] == valid_properties_data["url"] + assert props["categories"] == valid_properties_data["categories"] + assert props["sentence"] == valid_properties_data["sentence"] + assert props["paragraph"] == valid_properties_data["paragraph"] + assert props["version"] == int(valid_properties_data["version"]) + assert props["prettyVersion"] == valid_properties_data["prettyVersion"] + assert props["minRevision"] == int(valid_properties_data["minRevision"]) + assert props["maxRevision"] == int(valid_properties_data["maxRevision"]) + assert props["modes"] == valid_properties_data["modes"] def test_validate_new_minimal_required_data(self, minimal_properties_base_data): """Test validate_new with minimal data""" props = validate_new(minimal_properties_base_data) - assert props['name'] == minimal_properties_base_data['name'] - assert props['authors'] == minimal_properties_base_data['authors'] - assert props['url'] == minimal_properties_base_data['url'] - assert props['categories'] is None - assert props['sentence'] == minimal_properties_base_data['sentence'] - assert props['paragraph'] is None - assert props['version'] == int(minimal_properties_base_data['version']) - assert props['prettyVersion'] == minimal_properties_base_data['prettyVersion'] - assert props['minRevision'] == 0 # Default value - assert props['maxRevision'] == 0 # Default value - assert props['modes'] is None - + assert props["name"] == minimal_properties_base_data["name"] + assert props["authors"] == minimal_properties_base_data["authors"] + assert props["url"] == minimal_properties_base_data["url"] + assert props["categories"] is None + assert props["sentence"] == minimal_properties_base_data["sentence"] + assert props["paragraph"] is None + assert props["version"] == int(minimal_properties_base_data["version"]) + assert props["prettyVersion"] == minimal_properties_base_data["prettyVersion"] + assert props["minRevision"] == 0 # Default value + assert props["maxRevision"] == 0 # Default value + assert props["modes"] is None def test_validate_new_extra_fields_allowed(self, properties_with_extra_fields): """Test validate_new with extra fields""" props = validate_new(properties_with_extra_fields) - assert props['name'] == properties_with_extra_fields['name'] - assert props['customField'] == properties_with_extra_fields['customField'] - assert props['anotherExtra'] == properties_with_extra_fields['anotherExtra'] - + assert props["name"] == properties_with_extra_fields["name"] + assert props["customField"] == properties_with_extra_fields["customField"] + assert props["anotherExtra"] == properties_with_extra_fields["anotherExtra"] def test_validate_new_library_complete_data(self, valid_properties_data): """Test validate_new_library with complete data""" props = validate_new_library(valid_properties_data) - assert props['name'] == valid_properties_data['name'] - assert props['authors'] == valid_properties_data['authors'] - assert props['url'] == valid_properties_data['url'] - assert props['categories'] == valid_properties_data['categories'] - assert props['sentence'] == valid_properties_data['sentence'] - assert props['paragraph'] == valid_properties_data['paragraph'] - assert props['version'] == int(valid_properties_data['version']) - assert props['prettyVersion'] == valid_properties_data['prettyVersion'] - assert props['minRevision'] == int(valid_properties_data['minRevision']) - assert props['maxRevision'] == int(valid_properties_data['maxRevision']) - assert props['modes'] == valid_properties_data['modes'] - + assert props["name"] == valid_properties_data["name"] + assert props["authors"] == valid_properties_data["authors"] + assert props["url"] == valid_properties_data["url"] + assert props["categories"] == valid_properties_data["categories"] + assert props["sentence"] == valid_properties_data["sentence"] + assert props["paragraph"] == valid_properties_data["paragraph"] + assert props["version"] == int(valid_properties_data["version"]) + assert props["prettyVersion"] == valid_properties_data["prettyVersion"] + assert props["minRevision"] == int(valid_properties_data["minRevision"]) + assert props["maxRevision"] == int(valid_properties_data["maxRevision"]) + assert props["modes"] == valid_properties_data["modes"] def test_validate_new_library_minimal_required_data(self, minimal_properties_library_data): """Test validate_new_library with minimal data""" props = validate_new_library(minimal_properties_library_data) - assert props['name'] == minimal_properties_library_data['name'] - assert props['authors'] == minimal_properties_library_data['authors'] - assert props['url'] == minimal_properties_library_data['url'] - assert props['categories'] == minimal_properties_library_data['categories'] - assert props['sentence'] == minimal_properties_library_data['sentence'] - assert props['paragraph'] is None - assert props['version'] == int(minimal_properties_library_data['version']) - assert props['prettyVersion'] == minimal_properties_library_data['prettyVersion'] - assert props['minRevision'] == 0 # Default value - assert props['maxRevision'] == 0 # Default value - assert props['modes'] is None - + assert props["name"] == minimal_properties_library_data["name"] + assert props["authors"] == minimal_properties_library_data["authors"] + assert props["url"] == minimal_properties_library_data["url"] + assert props["categories"] == minimal_properties_library_data["categories"] + assert props["sentence"] == minimal_properties_library_data["sentence"] + assert props["paragraph"] is None + assert props["version"] == int(minimal_properties_library_data["version"]) + assert props["prettyVersion"] == minimal_properties_library_data["prettyVersion"] + assert props["minRevision"] == 0 # Default value + assert props["maxRevision"] == 0 # Default value + assert props["modes"] is None def test_validate_new_library_extra_fields_allowed(self, properties_with_extra_fields): """Test validate_new_library with extra fields""" props = validate_new_library(properties_with_extra_fields) - assert props['name'] == properties_with_extra_fields['name'] - assert props['customField'] == properties_with_extra_fields['customField'] - assert props['anotherExtra'] == properties_with_extra_fields['anotherExtra'] + assert props["name"] == properties_with_extra_fields["name"] + assert props["customField"] == properties_with_extra_fields["customField"] + assert props["anotherExtra"] == properties_with_extra_fields["anotherExtra"]