diff --git a/pkg/appview/handlers/repository.go b/pkg/appview/handlers/repository.go index 69fd7bc..63c77a5 100644 --- a/pkg/appview/handlers/repository.go +++ b/pkg/appview/handlers/repository.go @@ -291,10 +291,12 @@ func (h *RepositoryPageHandler) ServeHTTP(w http.ResponseWriter, r *http.Request // is a guess on our part. explicit := readmeURL != "" if readmeURL == "" && repo.SourceURL != "" { + // Only "main" is derived here. DeriveReadmeURL does no fetching: it + // interpolates the branch into a per-platform URL template and returns + // "" only for an unsupported platform, which is branch-independent. A + // main/master fallback therefore has to happen at fetch time, after a + // 404 (as the push and backfill paths do), not at derivation time. readmeURL = readme.DeriveReadmeURL(repo.SourceURL, "main") - if readmeURL == "" { - readmeURL = readme.DeriveReadmeURL(repo.SourceURL, "master") - } } if readmeURL != "" { html, raw, failed := resolveReadme(r.Context(), h.ReadmeFetcher, readmeURL, explicit) diff --git a/pkg/appview/jetstream/worker.go b/pkg/appview/jetstream/worker.go index 9f35aa4..3b91f66 100644 --- a/pkg/appview/jetstream/worker.go +++ b/pkg/appview/jetstream/worker.go @@ -663,6 +663,7 @@ func isRelevantCollection(collection string) bool { atproto.RepoPageCollection, atproto.SailorProfileCollection, atproto.StatsCollection, + atproto.DailyStatsCollection, atproto.CaptainCollection, atproto.CrewCollection, atproto.ScanCollection, diff --git a/pkg/appview/jetstream/worker_collections_test.go b/pkg/appview/jetstream/worker_collections_test.go new file mode 100644 index 0000000..674eca1 --- /dev/null +++ b/pkg/appview/jetstream/worker_collections_test.go @@ -0,0 +1,44 @@ +package jetstream + +import ( + "testing" + + "atcr.io/pkg/atproto" +) + +// TestIsRelevantCollection guards the gate that runs before ProcessRecord ever +// sees an event: a collection handled in processor.go or listed in backfill.go +// but missing here is silently dropped from the live stream. +func TestIsRelevantCollection(t *testing.T) { + tests := []struct { + name string + collection string + want bool + }{ + {"manifest", atproto.ManifestCollection, true}, + {"tag", atproto.TagCollection, true}, + {"star", atproto.StarCollection, true}, + {"repo page", atproto.RepoPageCollection, true}, + {"sailor profile", atproto.SailorProfileCollection, true}, + {"cumulative stats", atproto.StatsCollection, true}, + {"daily stats", atproto.DailyStatsCollection, true}, + {"captain", atproto.CaptainCollection, true}, + {"crew", atproto.CrewCollection, true}, + {"scan", atproto.ScanCollection, true}, + {"bluesky profile", BlueskyProfileCollection, true}, + + // Collections we deliberately ignore: no processor handler exists. + {"layer", atproto.LayerCollection, false}, + {"image config", atproto.ImageConfigCollection, false}, + {"bluesky post", atproto.BskyPostCollection, false}, + {"unknown", "com.example.unrelated", false}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := isRelevantCollection(tt.collection); got != tt.want { + t.Errorf("isRelevantCollection(%q) = %v, want %v", tt.collection, got, tt.want) + } + }) + } +}