From 30710d12dffb59413b7cc099c6e0c88c2ec4bb8a Mon Sep 17 00:00:00 2001 From: Bretton Date: Thu, 24 Sep 2026 20:54:24 -0700 Subject: [PATCH] test(posts): derive over-cap content fixtures from the service's cap The create and edit over-cap fixtures were numeric literals, so raising the cap to 500000 left the edit test sending under-cap content until 3cdbafa bumped the literal. The cap is now the exported constant posts.MaxContentLength, enforced once by the shared validator, and both fixtures are MaxContentLength+1. Changes: - Export MaxContentLength (500000 bytes, the postv2 lexicon's maxLength) - normalizeAndValidatePostContent uses it; value and error text unchanged - Create and update validation tests send MaxContentLength+1 Closes 2026-09-09-posts-update-cap-test-stale-after-50k-raise. Co-Authored-By: Claude Opus 5.5 (1M context) --- internal/core/posts/service.go | 12 ++++++++---- .../core/posts/service_create_validation_test.go | 7 ++++--- .../core/posts/service_update_validation_test.go | 2 +- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/internal/core/posts/service.go b/internal/core/posts/service.go index e15652b..1bcb3f7 100644 --- a/internal/core/posts/service.go +++ b/internal/core/posts/service.go @@ -935,6 +935,11 @@ const ( maxTagLength = 640 ) +// MaxContentLength is the post body cap, in BYTES — it matches the postv2 +// lexicon. Create and edit both enforce it through +// normalizeAndValidatePostContent. +const MaxContentLength = 500000 + // normalizeAndValidatePostContent is the definition of a well-formed Coves post, // and it is SHARED by the create and the edit path rather than duplicated across // them. @@ -989,13 +994,12 @@ const ( func normalizeAndValidatePostContent(post postContent) error { // Global content limits (from lexicon) const ( - maxContentLength = 500000 // 500k bytes - matches the postv2 lexicon - maxTitleLength = 3000 // 3k bytes + maxTitleLength = 3000 // 3k bytes ) - if post.Content != nil && len(*post.Content) > maxContentLength { + if post.Content != nil && len(*post.Content) > MaxContentLength { return NewValidationError("content", - fmt.Sprintf("content too long (max %d characters)", maxContentLength)) + fmt.Sprintf("content too long (max %d characters)", MaxContentLength)) } if post.Title != nil && len(*post.Title) > maxTitleLength { diff --git a/internal/core/posts/service_create_validation_test.go b/internal/core/posts/service_create_validation_test.go index c7a9753..8cb0280 100644 --- a/internal/core/posts/service_create_validation_test.go +++ b/internal/core/posts/service_create_validation_test.go @@ -203,9 +203,10 @@ func TestService_CreateResolvesTheCommunityAndValidatesTheRequest(t *testing.T) }) t.Run("Rejects content over the length limit", func(t *testing.T) { - // One byte past maxContentLength (500,000). The limit is what keeps a - // single record from being unbounded on the way to the PDS. - longContent := string(make([]byte, 500001)) + // One byte past the service's own content cap, derived from it so a + // change to the cap cannot leave this fixture behind. The limit is what + // keeps a single record from being unbounded on the way to the PDS. + longContent := string(make([]byte, posts.MaxContentLength+1)) err := createPost(posts.CreatePostRequest{ Community: community.DID, Content: &longContent, diff --git a/internal/core/posts/service_update_validation_test.go b/internal/core/posts/service_update_validation_test.go index 44bcb9a..06f0f6f 100644 --- a/internal/core/posts/service_update_validation_test.go +++ b/internal/core/posts/service_update_validation_test.go @@ -125,7 +125,7 @@ func TestService_UpdateRefusesContentCreateWouldHaveRefused(t *testing.T) { }, { name: "content past the lexicon's cap", - req: posts.UpdatePostRequest{Content: ptr(strings.Repeat("b", 500001))}, + req: posts.UpdatePostRequest{Content: ptr(strings.Repeat("b", posts.MaxContentLength+1))}, }, { name: "a label outside the allowlist", -- 2.51.2