# retoor import pytest from pydantic import ValidationError from devplacepy.services.sync.records import ( CommentRecord, PostRecord, SyncBatchForm, UserRecord, ) from devplacepy.services.sync.spec import LIMITS BASE = {"origin_uid": "r1", "created_at": "2025-01-01T00:00:00Z"} def test_updated_at_defaults_to_created_and_never_precedes_it(): record = PostRecord.model_validate( {**BASE, "author_uid": "u", "content": "ten chars!!"} ) assert record.updated_at == record.created_at earlier = PostRecord.model_validate( {**BASE, "author_uid": "u", "content": "ten chars!!", "updated_at": "2024-01-01"} ) assert earlier.updated_at == earlier.created_at def test_unknown_topic_becomes_random_and_known_is_kept(): base = {**BASE, "author_uid": "u", "content": "ten chars!!"} assert PostRecord.model_validate({**base, "topic": "zzz"}).topic == "random" assert PostRecord.model_validate({**base, "topic": "DevLog"}).topic == "devlog" @pytest.mark.parametrize( "payload", [ {"author_uid": "u", "content": "short"}, {"author_uid": "u", "content": "x" * (LIMITS.max_post_length + 1)}, {"author_uid": "", "content": "ten chars!!"}, {"content": "ten chars!!"}, {"author_uid": "u", "content": "ten chars!!", "title": "t" * (LIMITS.max_title_length + 1)}, ], ) def test_invalid_posts_are_rejected(payload): with pytest.raises(ValidationError): PostRecord.model_validate({**BASE, **payload}) def test_comment_parent_is_optional_and_post_is_required(): base = {**BASE, "author_uid": "u", "content": "fine"} assert CommentRecord.model_validate({**base, "post_uid": "p"}).parent_uid is None assert CommentRecord.model_validate({**base, "post_uid": "p", "parent_uid": ""}).parent_uid is None with pytest.raises(ValidationError): CommentRecord.model_validate(base) def test_user_urls_are_fixed_or_dropped(): record = UserRecord.model_validate( {**BASE, "username": "bob", "website": "example.com", "git_link": "not a url"} ) assert record.website == "https://example.com" assert record.git_link == "" def test_user_bio_over_limit_is_rejected(): with pytest.raises(ValidationError): UserRecord.model_validate({**BASE, "username": "bob", "bio": "x" * (LIMITS.max_bio_length + 1)}) def test_user_counters_and_credentials_are_not_fields(): known = UserRecord.known_fields() for name in ("email", "password_hash", "role", "xp", "level", "stars", "api_key"): assert name not in known assert "stars" not in PostRecord.known_fields() def test_envelope_normalizes_origin_and_bounds_batch(): form = SyncBatchForm(origin="Other.ORG", records=[{}]) assert form.origin == "other.org" with pytest.raises(ValidationError): SyncBatchForm(origin="other.org", records=[]) with pytest.raises(ValidationError): SyncBatchForm(origin="other.org", records=[{}] * (LIMITS.max_batch_records + 1)) with pytest.raises(ValidationError): SyncBatchForm(origin="bad origin", records=[{}])