From dd59b177c6120a432feebf2a071a72a39b695e66 Mon Sep 17 00:00:00 2001 From: Virgil Date: Mon, 30 Mar 2026 06:37:20 +0000 Subject: [PATCH] chore(ax): normalise test naming and usage annotations Co-Authored-By: Virgil --- agentci/config_test.go | 26 ++-- cmd/forge/cmd_sync_test.go | 8 +- cmd/gitea/cmd_sync_test.go | 8 +- collect/bitcointalk_http_test.go | 14 +-- collect/bitcointalk_test.go | 10 +- collect/collect_test.go | 4 +- collect/coverage_boost_test.go | 92 +++++++------- collect/coverage_phase2_test.go | 120 +++++++++---------- collect/events_test.go | 8 +- collect/excavate_extra_test.go | 10 +- collect/excavate_test.go | 14 +-- collect/github_test.go | 10 +- collect/market_extra_test.go | 14 +-- collect/market_test.go | 10 +- collect/papers_http_test.go | 16 +-- collect/papers_test.go | 12 +- collect/process_extra_test.go | 34 +++--- collect/process_test.go | 18 +-- collect/ratelimit.go | 5 + collect/ratelimit_test.go | 8 +- collect/state_extra_test.go | 8 +- collect/state_test.go | 8 +- forge/client.go | 6 + forge/client_test.go | 32 ++--- forge/config_test.go | 16 +-- forge/issues_test.go | 26 ++-- forge/labels_test.go | 18 +-- forge/meta.go | 3 + forge/meta_test.go | 6 +- forge/orgs_test.go | 6 +- forge/prs_test.go | 18 +-- forge/repos_test.go | 12 +- forge/webhooks_test.go | 4 +- gitea/client.go | 2 + gitea/client_test.go | 2 +- gitea/config_test.go | 16 +-- gitea/coverage_boost_test.go | 24 ++-- gitea/issues_test.go | 18 +-- gitea/meta.go | 3 + gitea/meta_test.go | 10 +- gitea/repos_test.go | 16 +-- jobrunner/forgejo/source_test.go | 4 +- jobrunner/handlers/completion.go | 3 + jobrunner/handlers/dispatch_test.go | 28 ++--- jobrunner/handlers/enable_auto_merge.go | 3 + jobrunner/handlers/enable_auto_merge_test.go | 6 +- jobrunner/handlers/publish_draft.go | 3 + jobrunner/handlers/publish_draft_test.go | 4 +- jobrunner/handlers/resolve_threads.go | 3 + jobrunner/handlers/resolve_threads_test.go | 2 +- jobrunner/handlers/send_fix_command.go | 3 + jobrunner/handlers/send_fix_command_test.go | 8 +- jobrunner/handlers/tick_parent.go | 3 + jobrunner/handlers/tick_parent_test.go | 2 +- jobrunner/journal_test.go | 8 +- jobrunner/poller_test.go | 6 +- jobrunner/types_test.go | 2 +- manifest/compile_test.go | 12 +- manifest/loader_test.go | 4 +- manifest/manifest.go | 1 + manifest/manifest_test.go | 18 +-- manifest/sign_test.go | 4 +- marketplace/builder_test.go | 24 ++-- marketplace/discovery_test.go | 18 +-- marketplace/installer_test.go | 16 +-- marketplace/marketplace_test.go | 2 +- pkg/api/provider_test.go | 2 +- plugin/installer.go | 3 + plugin/installer_test.go | 28 ++--- plugin/loader.go | 2 + plugin/loader_test.go | 10 +- plugin/manifest.go | 1 + plugin/manifest_test.go | 12 +- plugin/plugin.go | 5 + plugin/plugin_test.go | 4 +- plugin/registry.go | 6 + plugin/registry_test.go | 16 +-- repos/gitstate_test.go | 20 ++-- repos/kbconfig_test.go | 8 +- repos/registry_test.go | 46 +++---- repos/workconfig_test.go | 10 +- 81 files changed, 555 insertions(+), 500 deletions(-) diff --git a/agentci/config_test.go b/agentci/config_test.go index 04803e2..5c8f9ea 100644 --- a/agentci/config_test.go +++ b/agentci/config_test.go @@ -45,7 +45,7 @@ agentci: assert.Equal(t, "claude", agent.Runner) } -func TestLoadAgents_Good_MultipleAgents(t *testing.T) { +func TestLoadAgents_Good_MultipleAgents_Good(t *testing.T) { cfg := newTestConfig(t, ` agentci: agents: @@ -66,7 +66,7 @@ agentci: assert.Contains(t, agents, "local-codex") } -func TestLoadAgents_Good_SkipsInactive(t *testing.T) { +func TestLoadAgents_Good_SkipsInactive_Good(t *testing.T) { cfg := newTestConfig(t, ` agentci: agents: @@ -101,7 +101,7 @@ agentci: assert.Contains(t, active, "active-agent") } -func TestLoadAgents_Good_Defaults(t *testing.T) { +func TestLoadAgents_Good_Defaults_Good(t *testing.T) { cfg := newTestConfig(t, ` agentci: agents: @@ -119,14 +119,14 @@ agentci: assert.Equal(t, "claude", agent.Runner) } -func TestLoadAgents_Good_NoConfig(t *testing.T) { +func TestLoadAgents_Good_NoConfig_Good(t *testing.T) { cfg := newTestConfig(t, "") agents, err := LoadAgents(cfg) require.NoError(t, err) assert.Empty(t, agents) } -func TestLoadAgents_Bad_MissingHost(t *testing.T) { +func TestLoadAgents_Bad_MissingHost_Good(t *testing.T) { cfg := newTestConfig(t, ` agentci: agents: @@ -139,7 +139,7 @@ agentci: assert.Contains(t, err.Error(), "host is required") } -func TestLoadAgents_Good_WithDualRun(t *testing.T) { +func TestLoadAgents_Good_WithDualRun_Good(t *testing.T) { cfg := newTestConfig(t, ` agentci: agents: @@ -176,7 +176,7 @@ agentci: assert.Equal(t, "/etc/core/keys/clotho.pub", cc.SigningKeyPath) } -func TestLoadClothoConfig_Good_Defaults(t *testing.T) { +func TestLoadClothoConfig_Good_Defaults_Good(t *testing.T) { cfg := newTestConfig(t, "") cc, err := LoadClothoConfig(cfg) require.NoError(t, err) @@ -204,7 +204,7 @@ func TestSaveAgent_Good(t *testing.T) { assert.Equal(t, "haiku", agents["new-agent"].Model) } -func TestSaveAgent_Good_WithDualRun(t *testing.T) { +func TestSaveAgent_Good_WithDualRun_Good(t *testing.T) { cfg := newTestConfig(t, "") err := SaveAgent(cfg, "verified-agent", AgentConfig{ @@ -222,7 +222,7 @@ func TestSaveAgent_Good_WithDualRun(t *testing.T) { assert.True(t, agents["verified-agent"].DualRun) } -func TestSaveAgent_Good_OmitsEmptyOptionals(t *testing.T) { +func TestSaveAgent_Good_OmitsEmptyOptionals_Good(t *testing.T) { cfg := newTestConfig(t, "") err := SaveAgent(cfg, "minimal", AgentConfig{ @@ -256,7 +256,7 @@ agentci: assert.Contains(t, agents, "to-keep") } -func TestRemoveAgent_Bad_NotFound(t *testing.T) { +func TestRemoveAgent_Bad_NotFound_Good(t *testing.T) { cfg := newTestConfig(t, ` agentci: agents: @@ -269,7 +269,7 @@ agentci: assert.Contains(t, err.Error(), "not found") } -func TestRemoveAgent_Bad_NoAgents(t *testing.T) { +func TestRemoveAgent_Bad_NoAgents_Good(t *testing.T) { cfg := newTestConfig(t, "") err := RemoveAgent(cfg, "anything") assert.Error(t, err) @@ -294,14 +294,14 @@ agentci: assert.False(t, agents["agent-b"].Active) } -func TestListAgents_Good_Empty(t *testing.T) { +func TestListAgents_Good_Empty_Good(t *testing.T) { cfg := newTestConfig(t, "") agents, err := ListAgents(cfg) require.NoError(t, err) assert.Empty(t, agents) } -func TestRoundTrip_Good_SaveThenLoad(t *testing.T) { +func TestRoundTrip_Good_SaveThenLoad_Good(t *testing.T) { cfg := newTestConfig(t, "") err := SaveAgent(cfg, "alpha", AgentConfig{ diff --git a/cmd/forge/cmd_sync_test.go b/cmd/forge/cmd_sync_test.go index 4b87c6b..deac9dc 100644 --- a/cmd/forge/cmd_sync_test.go +++ b/cmd/forge/cmd_sync_test.go @@ -20,7 +20,7 @@ func TestBuildSyncRepoList_Good(t *testing.T) { assert.Equal(t, filepath.Join(basePath, "core"), repos[0].localPath) } -func TestBuildSyncRepoList_Bad_PathTraversal(t *testing.T) { +func TestBuildSyncRepoList_Bad_PathTraversal_Good(t *testing.T) { basePath := filepath.Join(t.TempDir(), "repos") _, err := buildSyncRepoList(nil, []string{"../escape"}, basePath) @@ -28,7 +28,7 @@ func TestBuildSyncRepoList_Bad_PathTraversal(t *testing.T) { assert.Contains(t, err.Error(), "invalid repo argument") } -func TestBuildSyncRepoList_Good_OwnerRepo(t *testing.T) { +func TestBuildSyncRepoList_Good_OwnerRepo_Good(t *testing.T) { basePath := filepath.Join(t.TempDir(), "repos") repos, err := buildSyncRepoList(nil, []string{"Host-UK/core"}, basePath) @@ -38,7 +38,7 @@ func TestBuildSyncRepoList_Good_OwnerRepo(t *testing.T) { assert.Equal(t, filepath.Join(basePath, "core"), repos[0].localPath) } -func TestBuildSyncRepoList_Bad_PathTraversal_OwnerRepo(t *testing.T) { +func TestBuildSyncRepoList_Bad_PathTraversal_OwnerRepo_Good(t *testing.T) { basePath := filepath.Join(t.TempDir(), "repos") _, err := buildSyncRepoList(nil, []string{"host-uk/../escape"}, basePath) @@ -46,7 +46,7 @@ func TestBuildSyncRepoList_Bad_PathTraversal_OwnerRepo(t *testing.T) { assert.Contains(t, err.Error(), "invalid repo argument") } -func TestBuildSyncRepoList_Bad_PathTraversal_OwnerRepoEncoded(t *testing.T) { +func TestBuildSyncRepoList_Bad_PathTraversal_OwnerRepoEncoded_Good(t *testing.T) { basePath := filepath.Join(t.TempDir(), "repos") _, err := buildSyncRepoList(nil, []string{"host-uk%2F..%2Fescape"}, basePath) diff --git a/cmd/gitea/cmd_sync_test.go b/cmd/gitea/cmd_sync_test.go index c6c1c9a..6d1b3ad 100644 --- a/cmd/gitea/cmd_sync_test.go +++ b/cmd/gitea/cmd_sync_test.go @@ -20,7 +20,7 @@ func TestBuildRepoList_Good(t *testing.T) { assert.Equal(t, filepath.Join(basePath, "core"), repos[0].localPath) } -func TestBuildRepoList_Bad_PathTraversal(t *testing.T) { +func TestBuildRepoList_Bad_PathTraversal_Good(t *testing.T) { basePath := filepath.Join(t.TempDir(), "repos") _, err := buildRepoList(nil, []string{"../escape"}, basePath) @@ -28,7 +28,7 @@ func TestBuildRepoList_Bad_PathTraversal(t *testing.T) { assert.Contains(t, err.Error(), "invalid repo argument") } -func TestBuildRepoList_Good_OwnerRepo(t *testing.T) { +func TestBuildRepoList_Good_OwnerRepo_Good(t *testing.T) { basePath := filepath.Join(t.TempDir(), "repos") repos, err := buildRepoList(nil, []string{"Host-UK/core"}, basePath) @@ -38,7 +38,7 @@ func TestBuildRepoList_Good_OwnerRepo(t *testing.T) { assert.Equal(t, filepath.Join(basePath, "core"), repos[0].localPath) } -func TestBuildRepoList_Bad_PathTraversal_OwnerRepo(t *testing.T) { +func TestBuildRepoList_Bad_PathTraversal_OwnerRepo_Good(t *testing.T) { basePath := filepath.Join(t.TempDir(), "repos") _, err := buildRepoList(nil, []string{"host-uk/../escape"}, basePath) @@ -46,7 +46,7 @@ func TestBuildRepoList_Bad_PathTraversal_OwnerRepo(t *testing.T) { assert.Contains(t, err.Error(), "invalid repo argument") } -func TestBuildRepoList_Bad_PathTraversal_OwnerRepoEncoded(t *testing.T) { +func TestBuildRepoList_Bad_PathTraversal_OwnerRepoEncoded_Good(t *testing.T) { basePath := filepath.Join(t.TempDir(), "repos") _, err := buildRepoList(nil, []string{"host-uk%2F..%2Fescape"}, basePath) diff --git a/collect/bitcointalk_http_test.go b/collect/bitcointalk_http_test.go index 6f04f46..a20541c 100644 --- a/collect/bitcointalk_http_test.go +++ b/collect/bitcointalk_http_test.go @@ -35,7 +35,7 @@ func sampleBTCTalkPage(count int) string { return page.String() } -func TestBitcoinTalkCollector_Collect_Good_OnePage(t *testing.T) { +func TestBitcoinTalkCollector_Collect_Good_OnePage_Good(t *testing.T) { // Serve a single page with 5 posts (< 20, so collection stops after one page). srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html") @@ -75,7 +75,7 @@ func TestBitcoinTalkCollector_Collect_Good_OnePage(t *testing.T) { } } -func TestBitcoinTalkCollector_Collect_Good_PageLimit(t *testing.T) { +func TestBitcoinTalkCollector_Collect_Good_PageLimit_Good(t *testing.T) { pageCount := 0 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { pageCount++ @@ -103,7 +103,7 @@ func TestBitcoinTalkCollector_Collect_Good_PageLimit(t *testing.T) { assert.Equal(t, 2, pageCount) } -func TestBitcoinTalkCollector_Collect_Good_CancelledContext(t *testing.T) { +func TestBitcoinTalkCollector_Collect_Good_CancelledContext_Good(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html") _, _ = w.Write([]byte(sampleBTCTalkPage(5))) @@ -127,7 +127,7 @@ func TestBitcoinTalkCollector_Collect_Good_CancelledContext(t *testing.T) { assert.Error(t, err) } -func TestBitcoinTalkCollector_Collect_Bad_ServerError(t *testing.T) { +func TestBitcoinTalkCollector_Collect_Bad_ServerError_Good(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusInternalServerError) })) @@ -151,7 +151,7 @@ func TestBitcoinTalkCollector_Collect_Bad_ServerError(t *testing.T) { assert.Equal(t, 1, result.Errors) } -func TestBitcoinTalkCollector_Collect_Good_EmitsEvents(t *testing.T) { +func TestBitcoinTalkCollector_Collect_Good_EmitsEvents_Good(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html") _, _ = w.Write([]byte(sampleBTCTalkPage(2))) @@ -209,7 +209,7 @@ func TestFetchPage_Good(t *testing.T) { assert.Len(t, posts, 3) } -func TestFetchPage_Bad_StatusCode(t *testing.T) { +func TestFetchPage_Bad_StatusCode_Good(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusForbidden) })) @@ -224,7 +224,7 @@ func TestFetchPage_Bad_StatusCode(t *testing.T) { assert.Error(t, err) } -func TestFetchPage_Bad_InvalidHTML(t *testing.T) { +func TestFetchPage_Bad_InvalidHTML_Good(t *testing.T) { // html.Parse is very forgiving, so serve an empty page. srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html") diff --git a/collect/bitcointalk_test.go b/collect/bitcointalk_test.go index 0d759e5..65e8511 100644 --- a/collect/bitcointalk_test.go +++ b/collect/bitcointalk_test.go @@ -15,12 +15,12 @@ func TestBitcoinTalkCollector_Name_Good(t *testing.T) { assert.Equal(t, "bitcointalk:12345", b.Name()) } -func TestBitcoinTalkCollector_Name_Good_URL(t *testing.T) { +func TestBitcoinTalkCollector_Name_Good_URL_Good(t *testing.T) { b := &BitcoinTalkCollector{URL: "https://bitcointalk.org/index.php?topic=12345.0"} assert.Equal(t, "bitcointalk:url", b.Name()) } -func TestBitcoinTalkCollector_Collect_Bad_NoTopicID(t *testing.T) { +func TestBitcoinTalkCollector_Collect_Bad_NoTopicID_Good(t *testing.T) { m := io.NewMockMedium() cfg := NewConfigWithMedium(m, "/output") @@ -29,7 +29,7 @@ func TestBitcoinTalkCollector_Collect_Bad_NoTopicID(t *testing.T) { assert.Error(t, err) } -func TestBitcoinTalkCollector_Collect_Good_DryRun(t *testing.T) { +func TestBitcoinTalkCollector_Collect_Good_DryRun_Good(t *testing.T) { m := io.NewMockMedium() cfg := NewConfigWithMedium(m, "/output") cfg.DryRun = true @@ -72,7 +72,7 @@ func TestParsePostsFromHTML_Good(t *testing.T) { assert.Contains(t, posts[1].Content, "Running bitcoin!") } -func TestParsePostsFromHTML_Good_Empty(t *testing.T) { +func TestParsePostsFromHTML_Good_Empty_Good(t *testing.T) { posts, err := ParsePostsFromHTML("") assert.NoError(t, err) assert.Empty(t, posts) @@ -86,7 +86,7 @@ func TestFormatPostMarkdown_Good(t *testing.T) { assert.Contains(t, md, "Hello, world!") } -func TestFormatPostMarkdown_Good_NoDate(t *testing.T) { +func TestFormatPostMarkdown_Good_NoDate_Good(t *testing.T) { md := FormatPostMarkdown(5, "user", "", "Content here") assert.Contains(t, md, "# Post 5 by user") diff --git a/collect/collect_test.go b/collect/collect_test.go index 643f82c..7d64982 100644 --- a/collect/collect_test.go +++ b/collect/collect_test.go @@ -56,13 +56,13 @@ func TestMergeResults_Good(t *testing.T) { assert.Len(t, merged.Files, 3) } -func TestMergeResults_Good_NilResults(t *testing.T) { +func TestMergeResults_Good_NilResults_Good(t *testing.T) { r1 := &Result{Items: 3} merged := MergeResults("test", r1, nil, nil) assert.Equal(t, 3, merged.Items) } -func TestMergeResults_Good_Empty(t *testing.T) { +func TestMergeResults_Good_Empty_Good(t *testing.T) { merged := MergeResults("empty") assert.Equal(t, 0, merged.Items) assert.Equal(t, 0, merged.Errors) diff --git a/collect/coverage_boost_test.go b/collect/coverage_boost_test.go index 623212f..db591cb 100644 --- a/collect/coverage_boost_test.go +++ b/collect/coverage_boost_test.go @@ -17,7 +17,7 @@ import ( // --- GitHub collector: context cancellation and orchestration --- -func TestGitHubCollector_Collect_Good_ContextCancelledInLoop(t *testing.T) { +func TestGitHubCollector_Collect_Good_ContextCancelledInLoop_Good(t *testing.T) { m := io.NewMockMedium() cfg := NewConfigWithMedium(m, "/output") cfg.DryRun = false @@ -33,7 +33,7 @@ func TestGitHubCollector_Collect_Good_ContextCancelledInLoop(t *testing.T) { assert.NotNil(t, result) } -func TestGitHubCollector_Collect_Good_IssuesOnlyDryRunProgress(t *testing.T) { +func TestGitHubCollector_Collect_Good_IssuesOnlyDryRunProgress_Good(t *testing.T) { m := io.NewMockMedium() cfg := NewConfigWithMedium(m, "/output") cfg.DryRun = true @@ -49,7 +49,7 @@ func TestGitHubCollector_Collect_Good_IssuesOnlyDryRunProgress(t *testing.T) { assert.GreaterOrEqual(t, progressCount, 1) } -func TestGitHubCollector_Collect_Good_PRsOnlyDryRunSkipsIssues(t *testing.T) { +func TestGitHubCollector_Collect_Good_PRsOnlyDryRunSkipsIssues_Good(t *testing.T) { m := io.NewMockMedium() cfg := NewConfigWithMedium(m, "/output") cfg.DryRun = true @@ -61,7 +61,7 @@ func TestGitHubCollector_Collect_Good_PRsOnlyDryRunSkipsIssues(t *testing.T) { assert.Equal(t, 0, result.Items) } -func TestGitHubCollector_Collect_Good_EmitsStartAndComplete(t *testing.T) { +func TestGitHubCollector_Collect_Good_EmitsStartAndComplete_Good(t *testing.T) { m := io.NewMockMedium() cfg := NewConfigWithMedium(m, "/output") cfg.DryRun = true @@ -78,7 +78,7 @@ func TestGitHubCollector_Collect_Good_EmitsStartAndComplete(t *testing.T) { assert.Equal(t, 1, completes) } -func TestGitHubCollector_Collect_Good_NilDispatcherHandled(t *testing.T) { +func TestGitHubCollector_Collect_Good_NilDispatcherHandled_Good(t *testing.T) { m := io.NewMockMedium() cfg := NewConfigWithMedium(m, "/output") cfg.DryRun = true @@ -91,7 +91,7 @@ func TestGitHubCollector_Collect_Good_NilDispatcherHandled(t *testing.T) { assert.Equal(t, 0, result.Items) } -func TestFormatIssueMarkdown_Good_NoBodyNoURL(t *testing.T) { +func TestFormatIssueMarkdown_Good_NoBodyNoURL_Good(t *testing.T) { issue := ghIssue{ Number: 1, Title: "No Body Issue", @@ -108,7 +108,7 @@ func TestFormatIssueMarkdown_Good_NoBodyNoURL(t *testing.T) { // --- Market collector: fetchJSON edge cases --- -func TestFetchJSON_Bad_NonJSONBody(t *testing.T) { +func TestFetchJSON_Bad_NonJSONBody_Good(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html") _, _ = w.Write([]byte(`not json`)) @@ -119,17 +119,17 @@ func TestFetchJSON_Bad_NonJSONBody(t *testing.T) { assert.Error(t, err) } -func TestFetchJSON_Bad_MalformedURL(t *testing.T) { +func TestFetchJSON_Bad_MalformedURL_Good(t *testing.T) { _, err := fetchJSON[coinData](context.Background(), "://bad-url") assert.Error(t, err) } -func TestFetchJSON_Bad_ServerUnavailable(t *testing.T) { +func TestFetchJSON_Bad_ServerUnavailable_Good(t *testing.T) { _, err := fetchJSON[coinData](context.Background(), "http://127.0.0.1:1") assert.Error(t, err) } -func TestFetchJSON_Bad_Non200StatusCode(t *testing.T) { +func TestFetchJSON_Bad_Non200StatusCode_Good(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNotFound) })) @@ -140,7 +140,7 @@ func TestFetchJSON_Bad_Non200StatusCode(t *testing.T) { assert.Contains(t, err.Error(), "unexpected status code") } -func TestMarketCollector_Collect_Bad_MissingCoinID(t *testing.T) { +func TestMarketCollector_Collect_Bad_MissingCoinID_Good(t *testing.T) { m := io.NewMockMedium() cfg := NewConfigWithMedium(m, "/output") @@ -150,7 +150,7 @@ func TestMarketCollector_Collect_Bad_MissingCoinID(t *testing.T) { assert.Contains(t, err.Error(), "coin ID is required") } -func TestMarketCollector_Collect_Good_NoDispatcher(t *testing.T) { +func TestMarketCollector_Collect_Good_NoDispatcher_Good(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") data := coinData{ID: "test", Symbol: "tst", Name: "Test", @@ -175,7 +175,7 @@ func TestMarketCollector_Collect_Good_NoDispatcher(t *testing.T) { assert.Equal(t, 2, result.Items) } -func TestMarketCollector_Collect_Bad_CurrentFetchFails(t *testing.T) { +func TestMarketCollector_Collect_Bad_CurrentFetchFails_Good(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusInternalServerError) })) @@ -197,7 +197,7 @@ func TestMarketCollector_Collect_Bad_CurrentFetchFails(t *testing.T) { assert.Equal(t, 1, result.Errors) } -func TestMarketCollector_CollectHistorical_Good_DefaultDays(t *testing.T) { +func TestMarketCollector_CollectHistorical_Good_DefaultDays_Good(t *testing.T) { callCount := 0 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { callCount++ @@ -229,7 +229,7 @@ func TestMarketCollector_CollectHistorical_Good_DefaultDays(t *testing.T) { assert.Equal(t, 3, result.Items) } -func TestMarketCollector_CollectHistorical_Good_WithRateLimiter(t *testing.T) { +func TestMarketCollector_CollectHistorical_Good_WithRateLimiter_Good(t *testing.T) { callCount := 0 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { callCount++ @@ -263,7 +263,7 @@ func TestMarketCollector_CollectHistorical_Good_WithRateLimiter(t *testing.T) { // --- State: error paths --- -func TestState_Load_Bad_MalformedJSON(t *testing.T) { +func TestState_Load_Bad_MalformedJSON_Good(t *testing.T) { m := io.NewMockMedium() m.Files["/state.json"] = `{invalid json` @@ -274,7 +274,7 @@ func TestState_Load_Bad_MalformedJSON(t *testing.T) { // --- Process: additional coverage for uncovered branches --- -func TestHTMLToMarkdown_Good_PreCodeBlock(t *testing.T) { +func TestHTMLToMarkdown_Good_PreCodeBlock_Good(t *testing.T) { input := `
some code here
` result, err := HTMLToMarkdown(input) require.NoError(t, err) @@ -282,7 +282,7 @@ func TestHTMLToMarkdown_Good_PreCodeBlock(t *testing.T) { assert.Contains(t, result, "some code here") } -func TestHTMLToMarkdown_Good_StrongAndEmElements(t *testing.T) { +func TestHTMLToMarkdown_Good_StrongAndEmElements_Good(t *testing.T) { input := `bold and italic` result, err := HTMLToMarkdown(input) require.NoError(t, err) @@ -290,21 +290,21 @@ func TestHTMLToMarkdown_Good_StrongAndEmElements(t *testing.T) { assert.Contains(t, result, "*italic*") } -func TestHTMLToMarkdown_Good_InlineCode(t *testing.T) { +func TestHTMLToMarkdown_Good_InlineCode_Good(t *testing.T) { input := `var x = 1` result, err := HTMLToMarkdown(input) require.NoError(t, err) assert.Contains(t, result, "`var x = 1`") } -func TestHTMLToMarkdown_Good_AnchorWithHref(t *testing.T) { +func TestHTMLToMarkdown_Good_AnchorWithHref_Good(t *testing.T) { input := `Click here` result, err := HTMLToMarkdown(input) require.NoError(t, err) assert.Contains(t, result, "[Click here](https://example.com)") } -func TestHTMLToMarkdown_Good_ScriptTagRemoved(t *testing.T) { +func TestHTMLToMarkdown_Good_ScriptTagRemoved_Good(t *testing.T) { input := `

Safe text

` result, err := HTMLToMarkdown(input) require.NoError(t, err) @@ -312,7 +312,7 @@ func TestHTMLToMarkdown_Good_ScriptTagRemoved(t *testing.T) { assert.NotContains(t, result, "alert") } -func TestHTMLToMarkdown_Good_H1H2H3Headers(t *testing.T) { +func TestHTMLToMarkdown_Good_H1H2H3Headers_Good(t *testing.T) { input := `

One

Two

Three

` result, err := HTMLToMarkdown(input) require.NoError(t, err) @@ -321,7 +321,7 @@ func TestHTMLToMarkdown_Good_H1H2H3Headers(t *testing.T) { assert.Contains(t, result, "### Three") } -func TestHTMLToMarkdown_Good_MultiParagraph(t *testing.T) { +func TestHTMLToMarkdown_Good_MultiParagraph_Good(t *testing.T) { input := `

First paragraph

Second paragraph

` result, err := HTMLToMarkdown(input) require.NoError(t, err) @@ -329,12 +329,12 @@ func TestHTMLToMarkdown_Good_MultiParagraph(t *testing.T) { assert.Contains(t, result, "Second paragraph") } -func TestJSONToMarkdown_Bad_Malformed(t *testing.T) { +func TestJSONToMarkdown_Bad_Malformed_Good(t *testing.T) { _, err := JSONToMarkdown(`{invalid}`) assert.Error(t, err) } -func TestJSONToMarkdown_Good_FlatObject(t *testing.T) { +func TestJSONToMarkdown_Good_FlatObject_Good(t *testing.T) { input := `{"name": "Alice", "age": 30}` result, err := JSONToMarkdown(input) require.NoError(t, err) @@ -342,7 +342,7 @@ func TestJSONToMarkdown_Good_FlatObject(t *testing.T) { assert.Contains(t, result, "**age:** 30") } -func TestJSONToMarkdown_Good_ScalarList(t *testing.T) { +func TestJSONToMarkdown_Good_ScalarList_Good(t *testing.T) { input := `["hello", "world"]` result, err := JSONToMarkdown(input) require.NoError(t, err) @@ -350,14 +350,14 @@ func TestJSONToMarkdown_Good_ScalarList(t *testing.T) { assert.Contains(t, result, "- world") } -func TestJSONToMarkdown_Good_ObjectContainingArray(t *testing.T) { +func TestJSONToMarkdown_Good_ObjectContainingArray_Good(t *testing.T) { input := `{"items": [1, 2, 3]}` result, err := JSONToMarkdown(input) require.NoError(t, err) assert.Contains(t, result, "**items:**") } -func TestProcessor_Process_Bad_MissingDir(t *testing.T) { +func TestProcessor_Process_Bad_MissingDir_Good(t *testing.T) { m := io.NewMockMedium() cfg := NewConfigWithMedium(m, "/output") @@ -367,7 +367,7 @@ func TestProcessor_Process_Bad_MissingDir(t *testing.T) { assert.Contains(t, err.Error(), "directory is required") } -func TestProcessor_Process_Good_DryRunEmitsProgress(t *testing.T) { +func TestProcessor_Process_Good_DryRunEmitsProgress_Good(t *testing.T) { m := io.NewMockMedium() cfg := NewConfigWithMedium(m, "/output") cfg.DryRun = true @@ -383,7 +383,7 @@ func TestProcessor_Process_Good_DryRunEmitsProgress(t *testing.T) { assert.Equal(t, 1, progressCount) } -func TestProcessor_Process_Good_SkipsUnsupportedExtension(t *testing.T) { +func TestProcessor_Process_Good_SkipsUnsupportedExtension_Good(t *testing.T) { m := io.NewMockMedium() m.Dirs["/input"] = true m.Files["/input/data.csv"] = `a,b,c` @@ -399,7 +399,7 @@ func TestProcessor_Process_Good_SkipsUnsupportedExtension(t *testing.T) { assert.Equal(t, 1, result.Skipped) } -func TestProcessor_Process_Good_MarkdownPassthroughTrimmed(t *testing.T) { +func TestProcessor_Process_Good_MarkdownPassthroughTrimmed_Good(t *testing.T) { m := io.NewMockMedium() m.Dirs["/input"] = true m.Files["/input/readme.md"] = `# Hello World ` @@ -418,7 +418,7 @@ func TestProcessor_Process_Good_MarkdownPassthroughTrimmed(t *testing.T) { assert.Equal(t, "# Hello World", content) } -func TestProcessor_Process_Good_HTMExtensionHandled(t *testing.T) { +func TestProcessor_Process_Good_HTMExtensionHandled_Good(t *testing.T) { m := io.NewMockMedium() m.Dirs["/input"] = true m.Files["/input/page.htm"] = `

HTM File

` @@ -433,7 +433,7 @@ func TestProcessor_Process_Good_HTMExtensionHandled(t *testing.T) { assert.Equal(t, 1, result.Items) } -func TestProcessor_Process_Good_NilDispatcherHandled(t *testing.T) { +func TestProcessor_Process_Good_NilDispatcherHandled_Good(t *testing.T) { m := io.NewMockMedium() m.Dirs["/input"] = true m.Files["/input/test.html"] = `

Text

` @@ -451,12 +451,12 @@ func TestProcessor_Process_Good_NilDispatcherHandled(t *testing.T) { // --- BitcoinTalk: additional edge cases --- -func TestBitcoinTalkCollector_Name_Good_EmptyTopicAndURL(t *testing.T) { +func TestBitcoinTalkCollector_Name_Good_EmptyTopicAndURL_Good(t *testing.T) { b := &BitcoinTalkCollector{} assert.Equal(t, "bitcointalk:", b.Name()) } -func TestBitcoinTalkCollector_Collect_Good_NilDispatcherHandled(t *testing.T) { +func TestBitcoinTalkCollector_Collect_Good_NilDispatcherHandled_Good(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html") _, _ = w.Write([]byte(sampleBTCTalkPage(2))) @@ -480,7 +480,7 @@ func TestBitcoinTalkCollector_Collect_Good_NilDispatcherHandled(t *testing.T) { assert.Equal(t, 2, result.Items) } -func TestBitcoinTalkCollector_Collect_Good_DryRunEmitsProgress(t *testing.T) { +func TestBitcoinTalkCollector_Collect_Good_DryRunEmitsProgress_Good(t *testing.T) { m := io.NewMockMedium() cfg := NewConfigWithMedium(m, "/output") cfg.DryRun = true @@ -496,7 +496,7 @@ func TestBitcoinTalkCollector_Collect_Good_DryRunEmitsProgress(t *testing.T) { assert.True(t, progressEmitted) } -func TestParsePostsFromHTML_Good_PostWithNoInnerContent(t *testing.T) { +func TestParsePostsFromHTML_Good_PostWithNoInnerContent_Good(t *testing.T) { htmlContent := `
user1
@@ -507,7 +507,7 @@ func TestParsePostsFromHTML_Good_PostWithNoInnerContent(t *testing.T) { assert.Empty(t, posts) } -func TestFormatPostMarkdown_Good_WithDateContent(t *testing.T) { +func TestFormatPostMarkdown_Good_WithDateContent_Good(t *testing.T) { md := FormatPostMarkdown(1, "alice", "2025-01-15", "Hello world") assert.Contains(t, md, "# Post 1 by alice") assert.Contains(t, md, "**Date:** 2025-01-15") @@ -516,7 +516,7 @@ func TestFormatPostMarkdown_Good_WithDateContent(t *testing.T) { // --- Papers collector: edge cases --- -func TestPapersCollector_Collect_Good_DryRunEmitsProgress(t *testing.T) { +func TestPapersCollector_Collect_Good_DryRunEmitsProgress_Good(t *testing.T) { m := io.NewMockMedium() cfg := NewConfigWithMedium(m, "/output") cfg.DryRun = true @@ -532,7 +532,7 @@ func TestPapersCollector_Collect_Good_DryRunEmitsProgress(t *testing.T) { assert.True(t, progressEmitted) } -func TestPapersCollector_Collect_Good_NilDispatcherIACR(t *testing.T) { +func TestPapersCollector_Collect_Good_NilDispatcherIACR_Good(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html") _, _ = w.Write([]byte(sampleIACRHTML)) @@ -556,7 +556,7 @@ func TestPapersCollector_Collect_Good_NilDispatcherIACR(t *testing.T) { assert.Equal(t, 2, result.Items) } -func TestArXivEntryToPaper_Good_NoAlternateLink(t *testing.T) { +func TestArXivEntryToPaper_Good_NoAlternateLink_Good(t *testing.T) { entry := arxivEntry{ ID: "http://arxiv.org/abs/2501.99999v1", Title: "No Alternate", @@ -571,7 +571,7 @@ func TestArXivEntryToPaper_Good_NoAlternateLink(t *testing.T) { // --- Excavator: additional edge cases --- -func TestExcavator_Run_Good_ResumeLoadError(t *testing.T) { +func TestExcavator_Run_Good_ResumeLoadError_Good(t *testing.T) { m := io.NewMockMedium() m.Files["/output/.collect-state.json"] = `{invalid` @@ -591,7 +591,7 @@ func TestExcavator_Run_Good_ResumeLoadError(t *testing.T) { // --- RateLimiter: additional edge cases --- -func TestRateLimiter_Wait_Good_QuickSuccessiveCallsAfterDelay(t *testing.T) { +func TestRateLimiter_Wait_Good_QuickSuccessiveCallsAfterDelay_Good(t *testing.T) { rl := NewRateLimiter() rl.SetDelay("fast", 1*time.Millisecond) @@ -610,7 +610,7 @@ func TestRateLimiter_Wait_Good_QuickSuccessiveCallsAfterDelay(t *testing.T) { // --- FormatMarketSummary: with empty market data values --- -func TestFormatMarketSummary_Good_ZeroRank(t *testing.T) { +func TestFormatMarketSummary_Good_ZeroRank_Good(t *testing.T) { data := &coinData{ Name: "Tiny Token", Symbol: "tiny", @@ -624,7 +624,7 @@ func TestFormatMarketSummary_Good_ZeroRank(t *testing.T) { assert.NotContains(t, summary, "Market Cap Rank") } -func TestFormatMarketSummary_Good_ZeroSupply(t *testing.T) { +func TestFormatMarketSummary_Good_ZeroSupply_Good(t *testing.T) { data := &coinData{ Name: "Zero Supply", Symbol: "zs", @@ -638,7 +638,7 @@ func TestFormatMarketSummary_Good_ZeroSupply(t *testing.T) { assert.NotContains(t, summary, "Total Supply") } -func TestFormatMarketSummary_Good_NoLastUpdated(t *testing.T) { +func TestFormatMarketSummary_Good_NoLastUpdated_Good(t *testing.T) { data := &coinData{ Name: "No Update", Symbol: "nu", diff --git a/collect/coverage_phase2_test.go b/collect/coverage_phase2_test.go index 4ece4f7..5c5476c 100644 --- a/collect/coverage_phase2_test.go +++ b/collect/coverage_phase2_test.go @@ -86,7 +86,7 @@ type errorLimiterWaiter struct{} // --- Processor: list error --- -func TestProcessor_Process_Bad_ListError(t *testing.T) { +func TestProcessor_Process_Bad_ListError_Good(t *testing.T) { em := &errorMedium{MockMedium: io.NewMockMedium(), listErr: testErr("list denied")} cfg := &Config{Output: em, OutputDir: "/output", Dispatcher: NewDispatcher()} @@ -98,7 +98,7 @@ func TestProcessor_Process_Bad_ListError(t *testing.T) { // --- Processor: ensureDir error --- -func TestProcessor_Process_Bad_EnsureDirError(t *testing.T) { +func TestProcessor_Process_Bad_EnsureDirError_Good(t *testing.T) { em := &errorMedium{MockMedium: io.NewMockMedium(), ensureDirErr: testErr("mkdir denied")} // Need to ensure List returns entries em.MockMedium.Dirs["/input"] = true @@ -114,7 +114,7 @@ func TestProcessor_Process_Bad_EnsureDirError(t *testing.T) { // --- Processor: context cancellation during processing --- -func TestProcessor_Process_Bad_ContextCancelledDuringLoop(t *testing.T) { +func TestProcessor_Process_Bad_ContextCancelledDuringLoop_Good(t *testing.T) { m := io.NewMockMedium() m.Dirs["/input"] = true m.Files["/input/a.html"] = "

Test

" @@ -133,7 +133,7 @@ func TestProcessor_Process_Bad_ContextCancelledDuringLoop(t *testing.T) { // --- Processor: read error during file processing --- -func TestProcessor_Process_Bad_ReadError(t *testing.T) { +func TestProcessor_Process_Bad_ReadError_Good(t *testing.T) { em := &errorMedium{MockMedium: io.NewMockMedium(), readErr: testErr("read denied")} em.MockMedium.Dirs["/input"] = true em.MockMedium.Files["/input/test.html"] = "

Test

" @@ -148,7 +148,7 @@ func TestProcessor_Process_Bad_ReadError(t *testing.T) { // --- Processor: JSON conversion error --- -func TestProcessor_Process_Bad_InvalidJSONFile(t *testing.T) { +func TestProcessor_Process_Bad_InvalidJSONFile_Good(t *testing.T) { m := io.NewMockMedium() m.Dirs["/input"] = true m.Files["/input/bad.json"] = "not valid json {" @@ -166,7 +166,7 @@ func TestProcessor_Process_Bad_InvalidJSONFile(t *testing.T) { // --- Processor: write error during output --- -func TestProcessor_Process_Bad_WriteError(t *testing.T) { +func TestProcessor_Process_Bad_WriteError_Good(t *testing.T) { em := &errorMedium{MockMedium: io.NewMockMedium(), writeErr: testErr("disk full")} em.MockMedium.Dirs["/input"] = true em.MockMedium.Files["/input/page.html"] = "

Title

" @@ -181,7 +181,7 @@ func TestProcessor_Process_Bad_WriteError(t *testing.T) { // --- Processor: successful processing with events --- -func TestProcessor_Process_Good_EmitsItemAndComplete(t *testing.T) { +func TestProcessor_Process_Good_EmitsItemAndComplete_Good(t *testing.T) { m := io.NewMockMedium() m.Dirs["/input"] = true m.Files["/input/page.html"] = "

Title

Body

" @@ -201,7 +201,7 @@ func TestProcessor_Process_Good_EmitsItemAndComplete(t *testing.T) { // --- Papers: with rate limiter that fails --- -func TestPapersCollector_CollectIACR_Bad_LimiterError(t *testing.T) { +func TestPapersCollector_CollectIACR_Bad_LimiterError_Good(t *testing.T) { m := io.NewMockMedium() cfg := NewConfigWithMedium(m, "/output") cfg.Limiter = NewRateLimiter() @@ -215,7 +215,7 @@ func TestPapersCollector_CollectIACR_Bad_LimiterError(t *testing.T) { assert.Error(t, err) } -func TestPapersCollector_CollectArXiv_Bad_LimiterError(t *testing.T) { +func TestPapersCollector_CollectArXiv_Bad_LimiterError_Good(t *testing.T) { m := io.NewMockMedium() cfg := NewConfigWithMedium(m, "/output") cfg.Limiter = NewRateLimiter() @@ -231,7 +231,7 @@ func TestPapersCollector_CollectArXiv_Bad_LimiterError(t *testing.T) { // --- Papers: IACR with bad HTML response --- -func TestPapersCollector_CollectIACR_Bad_InvalidHTML(t *testing.T) { +func TestPapersCollector_CollectIACR_Bad_InvalidHTML_Good(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html") // Serve valid-ish HTML but with no papers - the parse succeeds but returns empty. @@ -256,7 +256,7 @@ func TestPapersCollector_CollectIACR_Bad_InvalidHTML(t *testing.T) { // --- Papers: IACR write error --- -func TestPapersCollector_CollectIACR_Bad_WriteError(t *testing.T) { +func TestPapersCollector_CollectIACR_Bad_WriteError_Good(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html") _, _ = w.Write([]byte(sampleIACRHTML)) @@ -280,7 +280,7 @@ func TestPapersCollector_CollectIACR_Bad_WriteError(t *testing.T) { // --- Papers: IACR EnsureDir error --- -func TestPapersCollector_CollectIACR_Bad_EnsureDirError(t *testing.T) { +func TestPapersCollector_CollectIACR_Bad_EnsureDirError_Good(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html") _, _ = w.Write([]byte(sampleIACRHTML)) @@ -304,7 +304,7 @@ func TestPapersCollector_CollectIACR_Bad_EnsureDirError(t *testing.T) { // --- Papers: arXiv write error --- -func TestPapersCollector_CollectArXiv_Bad_WriteError(t *testing.T) { +func TestPapersCollector_CollectArXiv_Bad_WriteError_Good(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/xml") _, _ = w.Write([]byte(sampleArXivXML)) @@ -328,7 +328,7 @@ func TestPapersCollector_CollectArXiv_Bad_WriteError(t *testing.T) { // --- Papers: arXiv EnsureDir error --- -func TestPapersCollector_CollectArXiv_Bad_EnsureDirError(t *testing.T) { +func TestPapersCollector_CollectArXiv_Bad_EnsureDirError_Good(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/xml") _, _ = w.Write([]byte(sampleArXivXML)) @@ -352,7 +352,7 @@ func TestPapersCollector_CollectArXiv_Bad_EnsureDirError(t *testing.T) { // --- Papers: collectAll with dispatcher events --- -func TestPapersCollector_CollectAll_Good_WithDispatcher(t *testing.T) { +func TestPapersCollector_CollectAll_Good_WithDispatcher_Good(t *testing.T) { callCount := 0 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { callCount++ @@ -387,7 +387,7 @@ func TestPapersCollector_CollectAll_Good_WithDispatcher(t *testing.T) { // --- Papers: IACR with events on item emit --- -func TestPapersCollector_CollectIACR_Good_EmitsItemEvents(t *testing.T) { +func TestPapersCollector_CollectIACR_Good_EmitsItemEvents_Good(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html") _, _ = w.Write([]byte(sampleIACRHTML)) @@ -415,7 +415,7 @@ func TestPapersCollector_CollectIACR_Good_EmitsItemEvents(t *testing.T) { // --- Papers: arXiv with events on item emit --- -func TestPapersCollector_CollectArXiv_Good_EmitsItemEvents(t *testing.T) { +func TestPapersCollector_CollectArXiv_Good_EmitsItemEvents_Good(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/xml") _, _ = w.Write([]byte(sampleArXivXML)) @@ -443,7 +443,7 @@ func TestPapersCollector_CollectArXiv_Good_EmitsItemEvents(t *testing.T) { // --- Market: collectCurrent write error (summary path) --- -func TestMarketCollector_Collect_Bad_WriteError(t *testing.T) { +func TestMarketCollector_Collect_Bad_WriteError_Good(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") if strings.Contains(r.URL.Path, "/market_chart") { @@ -479,7 +479,7 @@ func TestMarketCollector_Collect_Bad_WriteError(t *testing.T) { // --- Market: EnsureDir error --- -func TestMarketCollector_Collect_Bad_EnsureDirError(t *testing.T) { +func TestMarketCollector_Collect_Bad_EnsureDirError_Good(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") _ = json.NewEncoder(w).Encode(coinData{ID: "bitcoin"}) @@ -502,7 +502,7 @@ func TestMarketCollector_Collect_Bad_EnsureDirError(t *testing.T) { // --- Market: collectCurrent with limiter wait error --- -func TestMarketCollector_Collect_Bad_LimiterError(t *testing.T) { +func TestMarketCollector_Collect_Bad_LimiterError_Good(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") _ = json.NewEncoder(w).Encode(coinData{ID: "bitcoin"}) @@ -529,7 +529,7 @@ func TestMarketCollector_Collect_Bad_LimiterError(t *testing.T) { // --- Market: collectHistorical with custom FromDate --- -func TestMarketCollector_Collect_Good_HistoricalCustomDate(t *testing.T) { +func TestMarketCollector_Collect_Good_HistoricalCustomDate_Good(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") if strings.Contains(r.URL.Path, "/market_chart") { @@ -564,7 +564,7 @@ func TestMarketCollector_Collect_Good_HistoricalCustomDate(t *testing.T) { // --- BitcoinTalk: EnsureDir error --- -func TestBitcoinTalkCollector_Collect_Bad_EnsureDirError(t *testing.T) { +func TestBitcoinTalkCollector_Collect_Bad_EnsureDirError_Good(t *testing.T) { em := &errorMedium{MockMedium: io.NewMockMedium(), ensureDirErr: testErr("mkdir denied")} cfg := &Config{Output: em, OutputDir: "/output", Dispatcher: NewDispatcher()} cfg.Limiter = nil @@ -577,7 +577,7 @@ func TestBitcoinTalkCollector_Collect_Bad_EnsureDirError(t *testing.T) { // --- BitcoinTalk: limiter error --- -func TestBitcoinTalkCollector_Collect_Bad_LimiterError(t *testing.T) { +func TestBitcoinTalkCollector_Collect_Bad_LimiterError_Good(t *testing.T) { m := io.NewMockMedium() cfg := NewConfigWithMedium(m, "/output") cfg.Limiter = NewRateLimiter() @@ -593,7 +593,7 @@ func TestBitcoinTalkCollector_Collect_Bad_LimiterError(t *testing.T) { // --- BitcoinTalk: write error during post saving --- -func TestBitcoinTalkCollector_Collect_Bad_WriteErrorOnPosts(t *testing.T) { +func TestBitcoinTalkCollector_Collect_Bad_WriteErrorOnPosts_Good(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html") _, _ = w.Write([]byte(sampleBTCTalkPage(3))) @@ -618,7 +618,7 @@ func TestBitcoinTalkCollector_Collect_Bad_WriteErrorOnPosts(t *testing.T) { // --- BitcoinTalk: fetchPage with bad HTTP status --- -func TestBitcoinTalkCollector_FetchPage_Bad_NonOKStatus(t *testing.T) { +func TestBitcoinTalkCollector_FetchPage_Bad_NonOKStatus_Good(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusForbidden) })) @@ -632,7 +632,7 @@ func TestBitcoinTalkCollector_FetchPage_Bad_NonOKStatus(t *testing.T) { // --- BitcoinTalk: fetchPage with request error --- -func TestBitcoinTalkCollector_FetchPage_Bad_RequestError(t *testing.T) { +func TestBitcoinTalkCollector_FetchPage_Bad_RequestError_Good(t *testing.T) { old := httpClient httpClient = &http.Client{Transport: &rewriteTransport{target: "http://127.0.0.1:1"}} // Connection refused defer func() { httpClient = old }() @@ -645,7 +645,7 @@ func TestBitcoinTalkCollector_FetchPage_Bad_RequestError(t *testing.T) { // --- BitcoinTalk: fetchPage with valid but empty page --- -func TestBitcoinTalkCollector_FetchPage_Good_EmptyPage(t *testing.T) { +func TestBitcoinTalkCollector_FetchPage_Good_EmptyPage_Good(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html") _, _ = w.Write([]byte("")) @@ -664,7 +664,7 @@ func TestBitcoinTalkCollector_FetchPage_Good_EmptyPage(t *testing.T) { // --- BitcoinTalk: Collect with fetch error + dispatcher --- -func TestBitcoinTalkCollector_Collect_Bad_FetchErrorWithDispatcher(t *testing.T) { +func TestBitcoinTalkCollector_Collect_Bad_FetchErrorWithDispatcher_Good(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusInternalServerError) })) @@ -691,7 +691,7 @@ func TestBitcoinTalkCollector_Collect_Bad_FetchErrorWithDispatcher(t *testing.T) // --- State: Save with a populated state --- -func TestState_Save_Good_RoundTrip(t *testing.T) { +func TestState_Save_Good_RoundTrip_Good(t *testing.T) { m := io.NewMockMedium() s := NewState(m, "/data/state.json") @@ -717,7 +717,7 @@ func TestState_Save_Good_RoundTrip(t *testing.T) { // --- GitHub: Collect with Repo set triggers collectIssues/collectPRs (which fail via gh) --- -func TestGitHubCollector_Collect_Bad_GhNotAuthenticated(t *testing.T) { +func TestGitHubCollector_Collect_Bad_GhNotAuthenticated_Good(t *testing.T) { m := io.NewMockMedium() cfg := NewConfigWithMedium(m, "/output") cfg.Limiter = nil @@ -737,7 +737,7 @@ func TestGitHubCollector_Collect_Bad_GhNotAuthenticated(t *testing.T) { // --- GitHub: Collect IssuesOnly triggers only issues, not PRs --- -func TestGitHubCollector_Collect_Bad_IssuesOnlyGhFails(t *testing.T) { +func TestGitHubCollector_Collect_Bad_IssuesOnlyGhFails_Good(t *testing.T) { m := io.NewMockMedium() cfg := NewConfigWithMedium(m, "/output") cfg.Limiter = nil @@ -750,7 +750,7 @@ func TestGitHubCollector_Collect_Bad_IssuesOnlyGhFails(t *testing.T) { // --- GitHub: Collect PRsOnly triggers only PRs, not issues --- -func TestGitHubCollector_Collect_Bad_PRsOnlyGhFails(t *testing.T) { +func TestGitHubCollector_Collect_Bad_PRsOnlyGhFails_Good(t *testing.T) { m := io.NewMockMedium() cfg := NewConfigWithMedium(m, "/output") cfg.Limiter = nil @@ -763,7 +763,7 @@ func TestGitHubCollector_Collect_Bad_PRsOnlyGhFails(t *testing.T) { // --- extractText: text before a br/p/div element adds newline --- -func TestExtractText_Good_TextBeforeBR(t *testing.T) { +func TestExtractText_Good_TextBeforeBR_Good(t *testing.T) { htmlStr := `
Hello
World

End

` posts, err := ParsePostsFromHTML(fmt.Sprintf(`
%s
`, "First text
Second text
Third text
")) @@ -777,7 +777,7 @@ func TestExtractText_Good_TextBeforeBR(t *testing.T) { // --- ParsePostsFromHTML: posts with full structure --- -func TestParsePostsFromHTML_Good_FullStructure(t *testing.T) { +func TestParsePostsFromHTML_Good_FullStructure_Good(t *testing.T) { htmlContent := `
TestAuthor
@@ -796,7 +796,7 @@ func TestParsePostsFromHTML_Good_FullStructure(t *testing.T) { // --- getChildrenText: nested element node path --- -func TestHTMLToMarkdown_Good_NestedElements(t *testing.T) { +func TestHTMLToMarkdown_Good_NestedElements_Good(t *testing.T) { // with nested triggers getChildrenText with non-text child nodes input := `

Nested Link

` result, err := HTMLToMarkdown(input) @@ -806,7 +806,7 @@ func TestHTMLToMarkdown_Good_NestedElements(t *testing.T) { // --- HTML: ordered list --- -func TestHTMLToMarkdown_Good_OL(t *testing.T) { +func TestHTMLToMarkdown_Good_OL_Good(t *testing.T) { input := `
  1. First
  2. Second
` result, err := HTMLToMarkdown(input) require.NoError(t, err) @@ -816,7 +816,7 @@ func TestHTMLToMarkdown_Good_OL(t *testing.T) { // --- HTML: blockquote --- -func TestHTMLToMarkdown_Good_BlockquoteElement(t *testing.T) { +func TestHTMLToMarkdown_Good_BlockquoteElement_Good(t *testing.T) { input := `
Quoted text
` result, err := HTMLToMarkdown(input) require.NoError(t, err) @@ -825,7 +825,7 @@ func TestHTMLToMarkdown_Good_BlockquoteElement(t *testing.T) { // --- HTML: hr --- -func TestHTMLToMarkdown_Good_HR(t *testing.T) { +func TestHTMLToMarkdown_Good_HR_Good(t *testing.T) { input := `

Before


After

` result, err := HTMLToMarkdown(input) require.NoError(t, err) @@ -834,7 +834,7 @@ func TestHTMLToMarkdown_Good_HR(t *testing.T) { // --- HTML: h4, h5, h6 --- -func TestHTMLToMarkdown_Good_AllHeadingLevels(t *testing.T) { +func TestHTMLToMarkdown_Good_AllHeadingLevels_Good(t *testing.T) { input := `

H4

H5
H6
` result, err := HTMLToMarkdown(input) require.NoError(t, err) @@ -845,7 +845,7 @@ func TestHTMLToMarkdown_Good_AllHeadingLevels(t *testing.T) { // --- HTML: link without href --- -func TestHTMLToMarkdown_Good_LinkNoHref(t *testing.T) { +func TestHTMLToMarkdown_Good_LinkNoHref_Good(t *testing.T) { input := `bare link text` result, err := HTMLToMarkdown(input) require.NoError(t, err) @@ -855,7 +855,7 @@ func TestHTMLToMarkdown_Good_LinkNoHref(t *testing.T) { // --- HTML: unordered list --- -func TestHTMLToMarkdown_Good_UL(t *testing.T) { +func TestHTMLToMarkdown_Good_UL_Good(t *testing.T) { input := `
  • Item A
  • Item B
` result, err := HTMLToMarkdown(input) require.NoError(t, err) @@ -865,7 +865,7 @@ func TestHTMLToMarkdown_Good_UL(t *testing.T) { // --- HTML: br tag --- -func TestHTMLToMarkdown_Good_BRTag(t *testing.T) { +func TestHTMLToMarkdown_Good_BRTag_Good(t *testing.T) { input := `

Line one
Line two

` result, err := HTMLToMarkdown(input) require.NoError(t, err) @@ -875,7 +875,7 @@ func TestHTMLToMarkdown_Good_BRTag(t *testing.T) { // --- HTML: style tag stripped --- -func TestHTMLToMarkdown_Good_StyleStripped(t *testing.T) { +func TestHTMLToMarkdown_Good_StyleStripped_Good(t *testing.T) { input := `

Clean

` result, err := HTMLToMarkdown(input) require.NoError(t, err) @@ -885,7 +885,7 @@ func TestHTMLToMarkdown_Good_StyleStripped(t *testing.T) { // --- HTML: i and b tags --- -func TestHTMLToMarkdown_Good_AlternateBoldItalic(t *testing.T) { +func TestHTMLToMarkdown_Good_AlternateBoldItalic_Good(t *testing.T) { input := `

bold and italic

` result, err := HTMLToMarkdown(input) require.NoError(t, err) @@ -895,7 +895,7 @@ func TestHTMLToMarkdown_Good_AlternateBoldItalic(t *testing.T) { // --- Market: collectCurrent with limiter that actually blocks --- -func TestMarketCollector_Collect_Bad_LimiterBlocksThenCancelled(t *testing.T) { +func TestMarketCollector_Collect_Bad_LimiterBlocksThenCancelled_Good(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") _ = json.NewEncoder(w).Encode(coinData{ID: "bitcoin", Symbol: "btc", Name: "Bitcoin", @@ -927,7 +927,7 @@ func TestMarketCollector_Collect_Bad_LimiterBlocksThenCancelled(t *testing.T) { // --- Papers: IACR with limiter that blocks --- -func TestPapersCollector_CollectIACR_Bad_LimiterBlocks(t *testing.T) { +func TestPapersCollector_CollectIACR_Bad_LimiterBlocks_Good(t *testing.T) { m := io.NewMockMedium() cfg := NewConfigWithMedium(m, "/output") cfg.Limiter = NewRateLimiter() @@ -944,7 +944,7 @@ func TestPapersCollector_CollectIACR_Bad_LimiterBlocks(t *testing.T) { // --- Papers: arXiv with limiter that blocks --- -func TestPapersCollector_CollectArXiv_Bad_LimiterBlocks(t *testing.T) { +func TestPapersCollector_CollectArXiv_Bad_LimiterBlocks_Good(t *testing.T) { m := io.NewMockMedium() cfg := NewConfigWithMedium(m, "/output") cfg.Limiter = NewRateLimiter() @@ -961,7 +961,7 @@ func TestPapersCollector_CollectArXiv_Bad_LimiterBlocks(t *testing.T) { // --- BitcoinTalk: limiter that blocks --- -func TestBitcoinTalkCollector_Collect_Bad_LimiterBlocks(t *testing.T) { +func TestBitcoinTalkCollector_Collect_Bad_LimiterBlocks_Good(t *testing.T) { m := io.NewMockMedium() cfg := NewConfigWithMedium(m, "/output") cfg.Limiter = NewRateLimiter() @@ -1021,7 +1021,7 @@ func (w *writeCountMedium) Exists(path string) bool { return w.MockMedium.Exists func (w *writeCountMedium) IsDir(path string) bool { return w.MockMedium.IsDir(path) } // Test that the summary.md write error in collectCurrent is handled. -func TestMarketCollector_Collect_Bad_SummaryWriteError(t *testing.T) { +func TestMarketCollector_Collect_Bad_SummaryWriteError_Good(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") if strings.Contains(r.URL.Path, "/market_chart") { @@ -1058,7 +1058,7 @@ func TestMarketCollector_Collect_Bad_SummaryWriteError(t *testing.T) { // --- Market: collectHistorical write error --- -func TestMarketCollector_Collect_Bad_HistoricalWriteError(t *testing.T) { +func TestMarketCollector_Collect_Bad_HistoricalWriteError_Good(t *testing.T) { callCount := 0 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { callCount++ @@ -1097,7 +1097,7 @@ func TestMarketCollector_Collect_Bad_HistoricalWriteError(t *testing.T) { // --- State: Save write error --- -func TestState_Save_Bad_WriteError(t *testing.T) { +func TestState_Save_Bad_WriteError_Good(t *testing.T) { em := &errorMedium{MockMedium: io.NewMockMedium(), writeErr: testErr("disk full")} s := NewState(em, "/state.json") s.Set("test", &StateEntry{Source: "test", Items: 1}) @@ -1109,7 +1109,7 @@ func TestState_Save_Bad_WriteError(t *testing.T) { // --- Excavator: collector with state error --- -func TestExcavator_Run_Bad_CollectorStateError(t *testing.T) { +func TestExcavator_Run_Bad_CollectorStateError_Good(t *testing.T) { m := io.NewMockMedium() cfg := NewConfigWithMedium(m, "/output") cfg.State = NewState(m, "/state.json") @@ -1131,7 +1131,7 @@ func TestExcavator_Run_Bad_CollectorStateError(t *testing.T) { // --- BitcoinTalk: page returns zero posts (empty content) --- -func TestBitcoinTalkCollector_Collect_Good_ZeroPostsPage(t *testing.T) { +func TestBitcoinTalkCollector_Collect_Good_ZeroPostsPage_Good(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html") // Valid HTML with no post divs at all @@ -1156,7 +1156,7 @@ func TestBitcoinTalkCollector_Collect_Good_ZeroPostsPage(t *testing.T) { // --- Excavator: state save error after collection --- -func TestExcavator_Run_Bad_StateSaveError(t *testing.T) { +func TestExcavator_Run_Bad_StateSaveError_Good(t *testing.T) { em := &errorMedium{MockMedium: io.NewMockMedium(), writeErr: testErr("state write failed")} cfg := &Config{ Output: io.NewMockMedium(), // Use regular medium for output @@ -1180,7 +1180,7 @@ func TestExcavator_Run_Bad_StateSaveError(t *testing.T) { // --- State: Load with read error --- -func TestState_Load_Bad_ReadError(t *testing.T) { +func TestState_Load_Bad_ReadError_Good(t *testing.T) { em := &errorMedium{MockMedium: io.NewMockMedium(), readErr: testErr("read denied")} em.MockMedium.Files["/state.json"] = "{}" // File exists but read will fail @@ -1192,7 +1192,7 @@ func TestState_Load_Bad_ReadError(t *testing.T) { // --- Papers: PaperSourceAll emits complete --- -func TestPapersCollector_CollectAll_Good_ArxivFailsWithIACR(t *testing.T) { +func TestPapersCollector_CollectAll_Good_ArxivFailsWithIACR_Good(t *testing.T) { callCount := 0 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { callCount++ @@ -1229,7 +1229,7 @@ func TestPapersCollector_CollectAll_Good_ArxivFailsWithIACR(t *testing.T) { // --- Papers: IACR with cancelled context (request creation fails) --- -func TestPapersCollector_CollectIACR_Bad_CancelledContextRequestFails(t *testing.T) { +func TestPapersCollector_CollectIACR_Bad_CancelledContextRequestFails_Good(t *testing.T) { // Don't set up any server - the request should fail because context is cancelled. m := io.NewMockMedium() cfg := NewConfigWithMedium(m, "/output") @@ -1245,7 +1245,7 @@ func TestPapersCollector_CollectIACR_Bad_CancelledContextRequestFails(t *testing // --- Papers: arXiv with cancelled context --- -func TestPapersCollector_CollectArXiv_Bad_CancelledContextRequestFails(t *testing.T) { +func TestPapersCollector_CollectArXiv_Bad_CancelledContextRequestFails_Good(t *testing.T) { m := io.NewMockMedium() cfg := NewConfigWithMedium(m, "/output") cfg.Limiter = nil @@ -1260,7 +1260,7 @@ func TestPapersCollector_CollectArXiv_Bad_CancelledContextRequestFails(t *testin // --- Market: collectHistorical limiter blocks --- -func TestMarketCollector_Collect_Bad_HistoricalLimiterBlocks(t *testing.T) { +func TestMarketCollector_Collect_Bad_HistoricalLimiterBlocks_Good(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") _ = json.NewEncoder(w).Encode(coinData{ @@ -1299,7 +1299,7 @@ func TestMarketCollector_Collect_Bad_HistoricalLimiterBlocks(t *testing.T) { // --- BitcoinTalk: fetchPage with invalid URL --- -func TestBitcoinTalkCollector_FetchPage_Bad_InvalidURL(t *testing.T) { +func TestBitcoinTalkCollector_FetchPage_Bad_InvalidURL_Good(t *testing.T) { b := &BitcoinTalkCollector{TopicID: "12345"} // Use a URL with control character that will fail NewRequestWithContext _, err := b.fetchPage(context.Background(), "http://\x7f/invalid") diff --git a/collect/events_test.go b/collect/events_test.go index b863e15..92dfd47 100644 --- a/collect/events_test.go +++ b/collect/events_test.go @@ -43,7 +43,7 @@ func TestDispatcher_On_Good(t *testing.T) { assert.Equal(t, 3, count, "All three handlers should be called") } -func TestDispatcher_Emit_Good_NoHandlers(t *testing.T) { +func TestDispatcher_Emit_Good_NoHandlers_Good(t *testing.T) { d := NewDispatcher() // Should not panic when emitting an event with no handlers @@ -56,7 +56,7 @@ func TestDispatcher_Emit_Good_NoHandlers(t *testing.T) { }) } -func TestDispatcher_Emit_Good_MultipleEventTypes(t *testing.T) { +func TestDispatcher_Emit_Good_MultipleEventTypes_Good(t *testing.T) { d := NewDispatcher() var starts, errors int @@ -71,7 +71,7 @@ func TestDispatcher_Emit_Good_MultipleEventTypes(t *testing.T) { assert.Equal(t, 1, errors) } -func TestDispatcher_Emit_Good_SetsTime(t *testing.T) { +func TestDispatcher_Emit_Good_SetsTime_Good(t *testing.T) { d := NewDispatcher() var received Event @@ -87,7 +87,7 @@ func TestDispatcher_Emit_Good_SetsTime(t *testing.T) { assert.True(t, received.Time.Before(after) || received.Time.Equal(after)) } -func TestDispatcher_Emit_Good_PreservesExistingTime(t *testing.T) { +func TestDispatcher_Emit_Good_PreservesExistingTime_Good(t *testing.T) { d := NewDispatcher() customTime := time.Date(2025, 6, 15, 12, 0, 0, 0, time.UTC) diff --git a/collect/excavate_extra_test.go b/collect/excavate_extra_test.go index c9b04bd..2f6adf6 100644 --- a/collect/excavate_extra_test.go +++ b/collect/excavate_extra_test.go @@ -12,7 +12,7 @@ import ( "github.com/stretchr/testify/require" ) -func TestExcavator_Run_Good_ResumeSkipsCompleted(t *testing.T) { +func TestExcavator_Run_Good_ResumeSkipsCompleted_Good(t *testing.T) { m := io.NewMockMedium() cfg := NewConfigWithMedium(m, "/output") cfg.Limiter = nil @@ -41,7 +41,7 @@ func TestExcavator_Run_Good_ResumeSkipsCompleted(t *testing.T) { assert.Equal(t, 1, result.Skipped) } -func TestExcavator_Run_Good_ResumeRunsIncomplete(t *testing.T) { +func TestExcavator_Run_Good_ResumeRunsIncomplete_Good(t *testing.T) { m := io.NewMockMedium() cfg := NewConfigWithMedium(m, "/output") cfg.Limiter = nil @@ -67,7 +67,7 @@ func TestExcavator_Run_Good_ResumeRunsIncomplete(t *testing.T) { assert.Equal(t, 5, result.Items) } -func TestExcavator_Run_Good_NilState(t *testing.T) { +func TestExcavator_Run_Good_NilState_Good(t *testing.T) { m := io.NewMockMedium() cfg := NewConfigWithMedium(m, "/output") cfg.State = nil @@ -85,7 +85,7 @@ func TestExcavator_Run_Good_NilState(t *testing.T) { assert.Equal(t, 3, result.Items) } -func TestExcavator_Run_Good_NilDispatcher(t *testing.T) { +func TestExcavator_Run_Good_NilDispatcher_Good(t *testing.T) { m := io.NewMockMedium() cfg := NewConfigWithMedium(m, "/output") cfg.Dispatcher = nil @@ -103,7 +103,7 @@ func TestExcavator_Run_Good_NilDispatcher(t *testing.T) { assert.Equal(t, 2, result.Items) } -func TestExcavator_Run_Good_ProgressEvents(t *testing.T) { +func TestExcavator_Run_Good_ProgressEvents_Good(t *testing.T) { m := io.NewMockMedium() cfg := NewConfigWithMedium(m, "/output") cfg.Limiter = nil diff --git a/collect/excavate_test.go b/collect/excavate_test.go index 3b9f8bc..f12ecbf 100644 --- a/collect/excavate_test.go +++ b/collect/excavate_test.go @@ -66,7 +66,7 @@ func TestExcavator_Run_Good(t *testing.T) { assert.Len(t, result.Files, 8) } -func TestExcavator_Run_Good_Empty(t *testing.T) { +func TestExcavator_Run_Good_Empty_Good(t *testing.T) { m := io.NewMockMedium() cfg := NewConfigWithMedium(m, "/output") @@ -77,7 +77,7 @@ func TestExcavator_Run_Good_Empty(t *testing.T) { assert.Equal(t, 0, result.Items) } -func TestExcavator_Run_Good_DryRun(t *testing.T) { +func TestExcavator_Run_Good_DryRun_Good(t *testing.T) { m := io.NewMockMedium() cfg := NewConfigWithMedium(m, "/output") cfg.DryRun = true @@ -98,7 +98,7 @@ func TestExcavator_Run_Good_DryRun(t *testing.T) { assert.Equal(t, 0, result.Items) } -func TestExcavator_Run_Good_ScanOnly(t *testing.T) { +func TestExcavator_Run_Good_ScanOnly_Good(t *testing.T) { m := io.NewMockMedium() cfg := NewConfigWithMedium(m, "/output") @@ -123,7 +123,7 @@ func TestExcavator_Run_Good_ScanOnly(t *testing.T) { assert.Contains(t, progressMessages[0], "source-a") } -func TestExcavator_Run_Good_WithErrors(t *testing.T) { +func TestExcavator_Run_Good_WithErrors_Good(t *testing.T) { m := io.NewMockMedium() cfg := NewConfigWithMedium(m, "/output") cfg.Limiter = nil @@ -146,7 +146,7 @@ func TestExcavator_Run_Good_WithErrors(t *testing.T) { assert.True(t, c3.called) } -func TestExcavator_Run_Good_CancelledContext(t *testing.T) { +func TestExcavator_Run_Good_CancelledContext_Good(t *testing.T) { m := io.NewMockMedium() cfg := NewConfigWithMedium(m, "/output") @@ -163,7 +163,7 @@ func TestExcavator_Run_Good_CancelledContext(t *testing.T) { assert.Error(t, err) } -func TestExcavator_Run_Good_SavesState(t *testing.T) { +func TestExcavator_Run_Good_SavesState_Good(t *testing.T) { m := io.NewMockMedium() cfg := NewConfigWithMedium(m, "/output") cfg.Limiter = nil @@ -184,7 +184,7 @@ func TestExcavator_Run_Good_SavesState(t *testing.T) { assert.Equal(t, "source-a", entry.Source) } -func TestExcavator_Run_Good_Events(t *testing.T) { +func TestExcavator_Run_Good_Events_Good(t *testing.T) { m := io.NewMockMedium() cfg := NewConfigWithMedium(m, "/output") cfg.Limiter = nil diff --git a/collect/github_test.go b/collect/github_test.go index 7deca98..ec10183 100644 --- a/collect/github_test.go +++ b/collect/github_test.go @@ -16,12 +16,12 @@ func TestGitHubCollector_Name_Good(t *testing.T) { assert.Equal(t, "github:host-uk/core", g.Name()) } -func TestGitHubCollector_Name_Good_OrgOnly(t *testing.T) { +func TestGitHubCollector_Name_Good_OrgOnly_Good(t *testing.T) { g := &GitHubCollector{Org: "host-uk"} assert.Equal(t, "github:host-uk", g.Name()) } -func TestGitHubCollector_Collect_Good_DryRun(t *testing.T) { +func TestGitHubCollector_Collect_Good_DryRun_Good(t *testing.T) { m := io.NewMockMedium() cfg := NewConfigWithMedium(m, "/output") cfg.DryRun = true @@ -40,7 +40,7 @@ func TestGitHubCollector_Collect_Good_DryRun(t *testing.T) { assert.True(t, progressEmitted, "Should emit progress event in dry-run mode") } -func TestGitHubCollector_Collect_Good_DryRun_IssuesOnly(t *testing.T) { +func TestGitHubCollector_Collect_Good_DryRun_IssuesOnly_Good(t *testing.T) { m := io.NewMockMedium() cfg := NewConfigWithMedium(m, "/output") cfg.DryRun = true @@ -52,7 +52,7 @@ func TestGitHubCollector_Collect_Good_DryRun_IssuesOnly(t *testing.T) { assert.Equal(t, 0, result.Items) } -func TestGitHubCollector_Collect_Good_DryRun_PRsOnly(t *testing.T) { +func TestGitHubCollector_Collect_Good_DryRun_PRsOnly_Good(t *testing.T) { m := io.NewMockMedium() cfg := NewConfigWithMedium(m, "/output") cfg.DryRun = true @@ -90,7 +90,7 @@ func TestFormatIssueMarkdown_Good(t *testing.T) { assert.Contains(t, md, "**URL:** https://github.com/test/repo/issues/42") } -func TestFormatIssueMarkdown_Good_NoLabels(t *testing.T) { +func TestFormatIssueMarkdown_Good_NoLabels_Good(t *testing.T) { issue := ghIssue{ Number: 1, Title: "Simple", diff --git a/collect/market_extra_test.go b/collect/market_extra_test.go index 12594f0..92dc8b7 100644 --- a/collect/market_extra_test.go +++ b/collect/market_extra_test.go @@ -14,7 +14,7 @@ import ( "github.com/stretchr/testify/require" ) -func TestMarketCollector_Collect_Good_HistoricalWithFromDate(t *testing.T) { +func TestMarketCollector_Collect_Good_HistoricalWithFromDate_Good(t *testing.T) { callCount := 0 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { callCount++ @@ -58,7 +58,7 @@ func TestMarketCollector_Collect_Good_HistoricalWithFromDate(t *testing.T) { assert.Equal(t, 3, result.Items) } -func TestMarketCollector_Collect_Good_HistoricalInvalidDate(t *testing.T) { +func TestMarketCollector_Collect_Good_HistoricalInvalidDate_Good(t *testing.T) { callCount := 0 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { callCount++ @@ -100,7 +100,7 @@ func TestMarketCollector_Collect_Good_HistoricalInvalidDate(t *testing.T) { assert.Equal(t, 3, result.Items) } -func TestMarketCollector_Collect_Bad_HistoricalServerError(t *testing.T) { +func TestMarketCollector_Collect_Bad_HistoricalServerError_Good(t *testing.T) { callCount := 0 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { callCount++ @@ -139,7 +139,7 @@ func TestMarketCollector_Collect_Bad_HistoricalServerError(t *testing.T) { assert.Equal(t, 1, result.Errors) // historical failed } -func TestMarketCollector_Collect_Good_EmitsEvents(t *testing.T) { +func TestMarketCollector_Collect_Good_EmitsEvents_Good(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") data := coinData{ @@ -174,7 +174,7 @@ func TestMarketCollector_Collect_Good_EmitsEvents(t *testing.T) { assert.Equal(t, 1, completes) } -func TestMarketCollector_Collect_Good_CancelledContext(t *testing.T) { +func TestMarketCollector_Collect_Good_CancelledContext_Good(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) })) @@ -199,7 +199,7 @@ func TestMarketCollector_Collect_Good_CancelledContext(t *testing.T) { assert.Equal(t, 1, result.Errors) } -func TestFormatMarketSummary_Good_AllFields(t *testing.T) { +func TestFormatMarketSummary_Good_AllFields_Good(t *testing.T) { data := &coinData{ Name: "Lethean", Symbol: "lthn", @@ -231,7 +231,7 @@ func TestFormatMarketSummary_Good_AllFields(t *testing.T) { assert.Contains(t, summary, "Last updated") } -func TestFormatMarketSummary_Good_Minimal(t *testing.T) { +func TestFormatMarketSummary_Good_Minimal_Good(t *testing.T) { data := &coinData{ Name: "Unknown", Symbol: "ukn", diff --git a/collect/market_test.go b/collect/market_test.go index 7366cb0..20ab023 100644 --- a/collect/market_test.go +++ b/collect/market_test.go @@ -18,7 +18,7 @@ func TestMarketCollector_Name_Good(t *testing.T) { assert.Equal(t, "market:bitcoin", m.Name()) } -func TestMarketCollector_Collect_Bad_NoCoinID(t *testing.T) { +func TestMarketCollector_Collect_Bad_NoCoinID_Good(t *testing.T) { mock := io.NewMockMedium() cfg := NewConfigWithMedium(mock, "/output") @@ -27,7 +27,7 @@ func TestMarketCollector_Collect_Bad_NoCoinID(t *testing.T) { assert.Error(t, err) } -func TestMarketCollector_Collect_Good_DryRun(t *testing.T) { +func TestMarketCollector_Collect_Good_DryRun_Good(t *testing.T) { mock := io.NewMockMedium() cfg := NewConfigWithMedium(mock, "/output") cfg.DryRun = true @@ -39,7 +39,7 @@ func TestMarketCollector_Collect_Good_DryRun(t *testing.T) { assert.Equal(t, 0, result.Items) } -func TestMarketCollector_Collect_Good_CurrentData(t *testing.T) { +func TestMarketCollector_Collect_Good_CurrentData_Good(t *testing.T) { // Set up a mock CoinGecko server server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { data := coinData{ @@ -94,7 +94,7 @@ func TestMarketCollector_Collect_Good_CurrentData(t *testing.T) { assert.Contains(t, summary, "42000.50") } -func TestMarketCollector_Collect_Good_Historical(t *testing.T) { +func TestMarketCollector_Collect_Good_Historical_Good(t *testing.T) { callCount := 0 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { callCount++ @@ -166,7 +166,7 @@ func TestFormatMarketSummary_Good(t *testing.T) { assert.Contains(t, summary, "Total Supply") } -func TestMarketCollector_Collect_Bad_ServerError(t *testing.T) { +func TestMarketCollector_Collect_Bad_ServerError_Good(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusInternalServerError) })) diff --git a/collect/papers_http_test.go b/collect/papers_http_test.go index 06f1c35..98760ab 100644 --- a/collect/papers_http_test.go +++ b/collect/papers_http_test.go @@ -111,7 +111,7 @@ func TestPapersCollector_CollectArXiv_Good(t *testing.T) { assert.Contains(t, content, "Alice") } -func TestPapersCollector_CollectArXiv_Good_WithCategory(t *testing.T) { +func TestPapersCollector_CollectArXiv_Good_WithCategory_Good(t *testing.T) { var capturedQuery string srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { capturedQuery = r.URL.RawQuery @@ -167,7 +167,7 @@ func TestPapersCollector_CollectAll_Good(t *testing.T) { assert.Equal(t, 4, result.Items) // 2 IACR + 2 arXiv } -func TestPapersCollector_CollectIACR_Bad_ServerError(t *testing.T) { +func TestPapersCollector_CollectIACR_Bad_ServerError_Good(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusInternalServerError) })) @@ -187,7 +187,7 @@ func TestPapersCollector_CollectIACR_Bad_ServerError(t *testing.T) { assert.Error(t, err) } -func TestPapersCollector_CollectArXiv_Bad_ServerError(t *testing.T) { +func TestPapersCollector_CollectArXiv_Bad_ServerError_Good(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusServiceUnavailable) })) @@ -207,7 +207,7 @@ func TestPapersCollector_CollectArXiv_Bad_ServerError(t *testing.T) { assert.Error(t, err) } -func TestPapersCollector_CollectArXiv_Bad_InvalidXML(t *testing.T) { +func TestPapersCollector_CollectArXiv_Bad_InvalidXML_Good(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/xml") _, _ = w.Write([]byte(`not xml at all`)) @@ -228,7 +228,7 @@ func TestPapersCollector_CollectArXiv_Bad_InvalidXML(t *testing.T) { assert.Error(t, err) } -func TestPapersCollector_CollectAll_Bad_BothFail(t *testing.T) { +func TestPapersCollector_CollectAll_Bad_BothFail_Good(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusInternalServerError) })) @@ -248,7 +248,7 @@ func TestPapersCollector_CollectAll_Bad_BothFail(t *testing.T) { assert.Error(t, err) } -func TestPapersCollector_CollectAll_Good_OneFails(t *testing.T) { +func TestPapersCollector_CollectAll_Good_OneFails_Good(t *testing.T) { callCount := 0 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { callCount++ @@ -297,7 +297,7 @@ func TestExtractIACRPapers_Good(t *testing.T) { assert.Equal(t, "Lattice Cryptography", papers[1].Title) } -func TestExtractIACRPapers_Good_Empty(t *testing.T) { +func TestExtractIACRPapers_Good_Empty_Good(t *testing.T) { doc, err := html.Parse(strings.NewReader(``)) require.NoError(t, err) @@ -305,7 +305,7 @@ func TestExtractIACRPapers_Good_Empty(t *testing.T) { assert.Empty(t, papers) } -func TestExtractIACRPapers_Good_NoTitle(t *testing.T) { +func TestExtractIACRPapers_Good_NoTitle_Good(t *testing.T) { doc, err := html.Parse(strings.NewReader(`
`)) require.NoError(t, err) diff --git a/collect/papers_test.go b/collect/papers_test.go index 29e9e40..c973587 100644 --- a/collect/papers_test.go +++ b/collect/papers_test.go @@ -15,17 +15,17 @@ func TestPapersCollector_Name_Good(t *testing.T) { assert.Equal(t, "papers:iacr", p.Name()) } -func TestPapersCollector_Name_Good_ArXiv(t *testing.T) { +func TestPapersCollector_Name_Good_ArXiv_Good(t *testing.T) { p := &PapersCollector{Source: PaperSourceArXiv} assert.Equal(t, "papers:arxiv", p.Name()) } -func TestPapersCollector_Name_Good_All(t *testing.T) { +func TestPapersCollector_Name_Good_All_Good(t *testing.T) { p := &PapersCollector{Source: PaperSourceAll} assert.Equal(t, "papers:all", p.Name()) } -func TestPapersCollector_Collect_Bad_NoQuery(t *testing.T) { +func TestPapersCollector_Collect_Bad_NoQuery_Good(t *testing.T) { m := io.NewMockMedium() cfg := NewConfigWithMedium(m, "/output") @@ -34,7 +34,7 @@ func TestPapersCollector_Collect_Bad_NoQuery(t *testing.T) { assert.Error(t, err) } -func TestPapersCollector_Collect_Bad_UnknownSource(t *testing.T) { +func TestPapersCollector_Collect_Bad_UnknownSource_Good(t *testing.T) { m := io.NewMockMedium() cfg := NewConfigWithMedium(m, "/output") @@ -43,7 +43,7 @@ func TestPapersCollector_Collect_Bad_UnknownSource(t *testing.T) { assert.Error(t, err) } -func TestPapersCollector_Collect_Good_DryRun(t *testing.T) { +func TestPapersCollector_Collect_Good_DryRun_Good(t *testing.T) { m := io.NewMockMedium() cfg := NewConfigWithMedium(m, "/output") cfg.DryRun = true @@ -74,7 +74,7 @@ func TestFormatPaperMarkdown_Good(t *testing.T) { assert.Contains(t, md, "zero-knowledge proofs") } -func TestFormatPaperMarkdown_Good_Minimal(t *testing.T) { +func TestFormatPaperMarkdown_Good_Minimal_Good(t *testing.T) { md := FormatPaperMarkdown("Title Only", nil, "", "", "", "") assert.Contains(t, md, "# Title Only") diff --git a/collect/process_extra_test.go b/collect/process_extra_test.go index dad769e..f7b75bb 100644 --- a/collect/process_extra_test.go +++ b/collect/process_extra_test.go @@ -11,7 +11,7 @@ import ( "github.com/stretchr/testify/require" ) -func TestHTMLToMarkdown_Good_OrderedList(t *testing.T) { +func TestHTMLToMarkdown_Good_OrderedList_Good(t *testing.T) { input := `
  1. First
  2. Second
  3. Third
` result, err := HTMLToMarkdown(input) require.NoError(t, err) @@ -20,7 +20,7 @@ func TestHTMLToMarkdown_Good_OrderedList(t *testing.T) { assert.Contains(t, result, "3. Third") } -func TestHTMLToMarkdown_Good_UnorderedList(t *testing.T) { +func TestHTMLToMarkdown_Good_UnorderedList_Good(t *testing.T) { input := `
  • Alpha
  • Beta
` result, err := HTMLToMarkdown(input) require.NoError(t, err) @@ -28,21 +28,21 @@ func TestHTMLToMarkdown_Good_UnorderedList(t *testing.T) { assert.Contains(t, result, "- Beta") } -func TestHTMLToMarkdown_Good_Blockquote(t *testing.T) { +func TestHTMLToMarkdown_Good_Blockquote_Good(t *testing.T) { input := `
A wise quote
` result, err := HTMLToMarkdown(input) require.NoError(t, err) assert.Contains(t, result, "> A wise quote") } -func TestHTMLToMarkdown_Good_HorizontalRule(t *testing.T) { +func TestHTMLToMarkdown_Good_HorizontalRule_Good(t *testing.T) { input := `

Before


After

` result, err := HTMLToMarkdown(input) require.NoError(t, err) assert.Contains(t, result, "---") } -func TestHTMLToMarkdown_Good_LinkWithoutHref(t *testing.T) { +func TestHTMLToMarkdown_Good_LinkWithoutHref_Good(t *testing.T) { input := `bare link text` result, err := HTMLToMarkdown(input) require.NoError(t, err) @@ -50,7 +50,7 @@ func TestHTMLToMarkdown_Good_LinkWithoutHref(t *testing.T) { assert.NotContains(t, result, "[") } -func TestHTMLToMarkdown_Good_H4H5H6(t *testing.T) { +func TestHTMLToMarkdown_Good_H4H5H6_Good(t *testing.T) { input := `

H4

H5
H6
` result, err := HTMLToMarkdown(input) require.NoError(t, err) @@ -59,7 +59,7 @@ func TestHTMLToMarkdown_Good_H4H5H6(t *testing.T) { assert.Contains(t, result, "###### H6") } -func TestHTMLToMarkdown_Good_StripsStyle(t *testing.T) { +func TestHTMLToMarkdown_Good_StripsStyle_Good(t *testing.T) { input := `

Clean

` result, err := HTMLToMarkdown(input) require.NoError(t, err) @@ -67,7 +67,7 @@ func TestHTMLToMarkdown_Good_StripsStyle(t *testing.T) { assert.NotContains(t, result, "color") } -func TestHTMLToMarkdown_Good_LineBreak(t *testing.T) { +func TestHTMLToMarkdown_Good_LineBreak_Good(t *testing.T) { input := `

Line one
Line two

` result, err := HTMLToMarkdown(input) require.NoError(t, err) @@ -75,7 +75,7 @@ func TestHTMLToMarkdown_Good_LineBreak(t *testing.T) { assert.Contains(t, result, "Line two") } -func TestHTMLToMarkdown_Good_NestedBoldItalic(t *testing.T) { +func TestHTMLToMarkdown_Good_NestedBoldItalic_Good(t *testing.T) { input := `bold text and italic text` result, err := HTMLToMarkdown(input) require.NoError(t, err) @@ -83,7 +83,7 @@ func TestHTMLToMarkdown_Good_NestedBoldItalic(t *testing.T) { assert.Contains(t, result, "*italic text*") } -func TestJSONToMarkdown_Good_NestedObject(t *testing.T) { +func TestJSONToMarkdown_Good_NestedObject_Good(t *testing.T) { input := `{"outer": {"inner_key": "inner_value"}}` result, err := JSONToMarkdown(input) require.NoError(t, err) @@ -91,7 +91,7 @@ func TestJSONToMarkdown_Good_NestedObject(t *testing.T) { assert.Contains(t, result, "**inner_key:** inner_value") } -func TestJSONToMarkdown_Good_NestedArray(t *testing.T) { +func TestJSONToMarkdown_Good_NestedArray_Good(t *testing.T) { input := `[["a", "b"], ["c"]]` result, err := JSONToMarkdown(input) require.NoError(t, err) @@ -100,14 +100,14 @@ func TestJSONToMarkdown_Good_NestedArray(t *testing.T) { assert.Contains(t, result, "b") } -func TestJSONToMarkdown_Good_ScalarValue(t *testing.T) { +func TestJSONToMarkdown_Good_ScalarValue_Good(t *testing.T) { input := `42` result, err := JSONToMarkdown(input) require.NoError(t, err) assert.Contains(t, result, "42") } -func TestJSONToMarkdown_Good_ArrayOfObjects(t *testing.T) { +func TestJSONToMarkdown_Good_ArrayOfObjects_Good(t *testing.T) { input := `[{"name": "Alice"}, {"name": "Bob"}]` result, err := JSONToMarkdown(input) require.NoError(t, err) @@ -117,7 +117,7 @@ func TestJSONToMarkdown_Good_ArrayOfObjects(t *testing.T) { assert.Contains(t, result, "Bob") } -func TestProcessor_Process_Good_CancelledContext(t *testing.T) { +func TestProcessor_Process_Good_CancelledContext_Good(t *testing.T) { m := io.NewMockMedium() m.Dirs["/input"] = true m.Files["/input/file.html"] = `

Test

` @@ -133,7 +133,7 @@ func TestProcessor_Process_Good_CancelledContext(t *testing.T) { assert.Error(t, err) } -func TestProcessor_Process_Good_EmitsEvents(t *testing.T) { +func TestProcessor_Process_Good_EmitsEvents_Good(t *testing.T) { m := io.NewMockMedium() m.Dirs["/input"] = true m.Files["/input/a.html"] = `

Title

` @@ -157,7 +157,7 @@ func TestProcessor_Process_Good_EmitsEvents(t *testing.T) { assert.Equal(t, 1, completes) } -func TestProcessor_Process_Good_BadHTML(t *testing.T) { +func TestProcessor_Process_Good_BadHTML_Good(t *testing.T) { m := io.NewMockMedium() m.Dirs["/input"] = true // html.Parse is very tolerant, so even bad HTML will parse. But we test @@ -174,7 +174,7 @@ func TestProcessor_Process_Good_BadHTML(t *testing.T) { assert.Equal(t, 1, result.Items) } -func TestProcessor_Process_Good_BadJSON(t *testing.T) { +func TestProcessor_Process_Good_BadJSON_Good(t *testing.T) { m := io.NewMockMedium() m.Dirs["/input"] = true m.Files["/input/bad.json"] = `not valid json` diff --git a/collect/process_test.go b/collect/process_test.go index daf22aa..12b9fed 100644 --- a/collect/process_test.go +++ b/collect/process_test.go @@ -15,7 +15,7 @@ func TestProcessor_Name_Good(t *testing.T) { assert.Equal(t, "process:github", p.Name()) } -func TestProcessor_Process_Bad_NoDir(t *testing.T) { +func TestProcessor_Process_Bad_NoDir_Good(t *testing.T) { m := io.NewMockMedium() cfg := NewConfigWithMedium(m, "/output") @@ -24,7 +24,7 @@ func TestProcessor_Process_Bad_NoDir(t *testing.T) { assert.Error(t, err) } -func TestProcessor_Process_Good_DryRun(t *testing.T) { +func TestProcessor_Process_Good_DryRun_Good(t *testing.T) { m := io.NewMockMedium() cfg := NewConfigWithMedium(m, "/output") cfg.DryRun = true @@ -36,7 +36,7 @@ func TestProcessor_Process_Good_DryRun(t *testing.T) { assert.Equal(t, 0, result.Items) } -func TestProcessor_Process_Good_HTMLFiles(t *testing.T) { +func TestProcessor_Process_Good_HTMLFiles_Good(t *testing.T) { m := io.NewMockMedium() m.Dirs["/input"] = true m.Files["/input/page.html"] = `

Hello

World

` @@ -57,7 +57,7 @@ func TestProcessor_Process_Good_HTMLFiles(t *testing.T) { assert.Contains(t, content, "World") } -func TestProcessor_Process_Good_JSONFiles(t *testing.T) { +func TestProcessor_Process_Good_JSONFiles_Good(t *testing.T) { m := io.NewMockMedium() m.Dirs["/input"] = true m.Files["/input/data.json"] = `{"name": "Bitcoin", "price": 42000}` @@ -77,7 +77,7 @@ func TestProcessor_Process_Good_JSONFiles(t *testing.T) { assert.Contains(t, content, "Bitcoin") } -func TestProcessor_Process_Good_MarkdownPassthrough(t *testing.T) { +func TestProcessor_Process_Good_MarkdownPassthrough_Good(t *testing.T) { m := io.NewMockMedium() m.Dirs["/input"] = true m.Files["/input/readme.md"] = "# Already Markdown\n\nThis is already formatted." @@ -96,7 +96,7 @@ func TestProcessor_Process_Good_MarkdownPassthrough(t *testing.T) { assert.Contains(t, content, "# Already Markdown") } -func TestProcessor_Process_Good_SkipUnknownTypes(t *testing.T) { +func TestProcessor_Process_Good_SkipUnknownTypes_Good(t *testing.T) { m := io.NewMockMedium() m.Dirs["/input"] = true m.Files["/input/image.png"] = "binary data" @@ -172,7 +172,7 @@ func TestHTMLToMarkdown_Good(t *testing.T) { } } -func TestHTMLToMarkdown_Good_StripsScripts(t *testing.T) { +func TestHTMLToMarkdown_Good_StripsScripts_Good(t *testing.T) { input := `

Clean

` result, err := HTMLToMarkdown(input) assert.NoError(t, err) @@ -190,14 +190,14 @@ func TestJSONToMarkdown_Good(t *testing.T) { assert.Contains(t, result, "42") } -func TestJSONToMarkdown_Good_Array(t *testing.T) { +func TestJSONToMarkdown_Good_Array_Good(t *testing.T) { input := `[{"id": 1}, {"id": 2}]` result, err := JSONToMarkdown(input) assert.NoError(t, err) assert.Contains(t, result, "# Data") } -func TestJSONToMarkdown_Bad_InvalidJSON(t *testing.T) { +func TestJSONToMarkdown_Bad_InvalidJSON_Good(t *testing.T) { _, err := JSONToMarkdown("not json") assert.Error(t, err) } diff --git a/collect/ratelimit.go b/collect/ratelimit.go index 4356acf..2406c5b 100644 --- a/collect/ratelimit.go +++ b/collect/ratelimit.go @@ -43,6 +43,7 @@ func NewRateLimiter() *RateLimiter { // Wait blocks until the rate limit allows the next request for the given source. // It respects context cancellation. +// Usage: Wait(...) func (r *RateLimiter) Wait(ctx context.Context, source string) error { r.mu.Lock() delay, ok := r.delays[source] @@ -77,6 +78,7 @@ func (r *RateLimiter) Wait(ctx context.Context, source string) error { } // SetDelay sets the delay for a source. +// Usage: SetDelay(...) func (r *RateLimiter) SetDelay(source string, d time.Duration) { r.mu.Lock() defer r.mu.Unlock() @@ -84,6 +86,7 @@ func (r *RateLimiter) SetDelay(source string, d time.Duration) { } // GetDelay returns the delay configured for a source. +// Usage: GetDelay(...) func (r *RateLimiter) GetDelay(source string) time.Duration { r.mu.Lock() defer r.mu.Unlock() @@ -97,6 +100,7 @@ func (r *RateLimiter) GetDelay(source string) time.Duration { // Returns used and limit counts. Auto-pauses at 75% usage by increasing // the GitHub rate limit delay. // Deprecated: Use CheckGitHubRateLimitCtx for context-aware cancellation. +// Usage: CheckGitHubRateLimit(...) func (r *RateLimiter) CheckGitHubRateLimit() (used, limit int, err error) { return r.CheckGitHubRateLimitCtx(context.Background()) } @@ -104,6 +108,7 @@ func (r *RateLimiter) CheckGitHubRateLimit() (used, limit int, err error) { // CheckGitHubRateLimitCtx checks GitHub API rate limit status via gh api with context support. // Returns used and limit counts. Auto-pauses at 75% usage by increasing // the GitHub rate limit delay. +// Usage: CheckGitHubRateLimitCtx(...) func (r *RateLimiter) CheckGitHubRateLimitCtx(ctx context.Context) (used, limit int, err error) { cmd := exec.CommandContext(ctx, "gh", "api", "rate_limit", "--jq", ".rate | \"\\(.used) \\(.limit)\"") out, err := cmd.Output() diff --git a/collect/ratelimit_test.go b/collect/ratelimit_test.go index 45ad9bb..51d5be5 100644 --- a/collect/ratelimit_test.go +++ b/collect/ratelimit_test.go @@ -29,7 +29,7 @@ func TestRateLimiter_Wait_Good(t *testing.T) { assert.GreaterOrEqual(t, time.Since(start), 40*time.Millisecond) // allow small timing variance } -func TestRateLimiter_Wait_Bad_ContextCancelled(t *testing.T) { +func TestRateLimiter_Wait_Bad_ContextCancelled_Good(t *testing.T) { rl := NewRateLimiter() rl.SetDelay("test", 5*time.Second) @@ -53,7 +53,7 @@ func TestRateLimiter_SetDelay_Good(t *testing.T) { assert.Equal(t, 3*time.Second, rl.GetDelay("custom")) } -func TestRateLimiter_GetDelay_Good_Defaults(t *testing.T) { +func TestRateLimiter_GetDelay_Good_Defaults_Good(t *testing.T) { rl := NewRateLimiter() assert.Equal(t, 500*time.Millisecond, rl.GetDelay("github")) @@ -62,13 +62,13 @@ func TestRateLimiter_GetDelay_Good_Defaults(t *testing.T) { assert.Equal(t, 1*time.Second, rl.GetDelay("iacr")) } -func TestRateLimiter_GetDelay_Good_UnknownSource(t *testing.T) { +func TestRateLimiter_GetDelay_Good_UnknownSource_Good(t *testing.T) { rl := NewRateLimiter() // Unknown sources should get the default 500ms delay assert.Equal(t, 500*time.Millisecond, rl.GetDelay("unknown")) } -func TestRateLimiter_Wait_Good_UnknownSource(t *testing.T) { +func TestRateLimiter_Wait_Good_UnknownSource_Good(t *testing.T) { rl := NewRateLimiter() ctx := context.Background() diff --git a/collect/state_extra_test.go b/collect/state_extra_test.go index a02aee3..0428ae3 100644 --- a/collect/state_extra_test.go +++ b/collect/state_extra_test.go @@ -10,7 +10,7 @@ import ( "github.com/stretchr/testify/require" ) -func TestState_Get_Good_ReturnsCopy(t *testing.T) { +func TestState_Get_Good_ReturnsCopy_Good(t *testing.T) { m := io.NewMockMedium() s := NewState(m, "/state.json") @@ -26,7 +26,7 @@ func TestState_Get_Good_ReturnsCopy(t *testing.T) { assert.Equal(t, 5, again.Items, "internal state should not be mutated") } -func TestState_Save_Good_WritesJSON(t *testing.T) { +func TestState_Save_Good_WritesJSON_Good(t *testing.T) { m := io.NewMockMedium() s := NewState(m, "/data/state.json") @@ -42,7 +42,7 @@ func TestState_Save_Good_WritesJSON(t *testing.T) { assert.Contains(t, content, `"abc"`) } -func TestState_Load_Good_NullJSON(t *testing.T) { +func TestState_Load_Good_NullJSON_Good(t *testing.T) { m := io.NewMockMedium() m.Files["/state.json"] = "null" @@ -55,7 +55,7 @@ func TestState_Load_Good_NullJSON(t *testing.T) { assert.False(t, ok) } -func TestState_SaveLoad_Good_WithCursor(t *testing.T) { +func TestState_SaveLoad_Good_WithCursor_Good(t *testing.T) { m := io.NewMockMedium() s := NewState(m, "/state.json") diff --git a/collect/state_test.go b/collect/state_test.go index 170b77f..f149ccf 100644 --- a/collect/state_test.go +++ b/collect/state_test.go @@ -75,7 +75,7 @@ func TestState_SaveLoad_Good(t *testing.T) { assert.True(t, now.Equal(got.LastRun)) } -func TestState_Load_Good_NoFile(t *testing.T) { +func TestState_Load_Good_NoFile_Good(t *testing.T) { m := io.NewMockMedium() s := NewState(m, "/nonexistent.json") @@ -88,7 +88,7 @@ func TestState_Load_Good_NoFile(t *testing.T) { assert.False(t, ok) } -func TestState_Load_Bad_InvalidJSON(t *testing.T) { +func TestState_Load_Bad_InvalidJSON_Good(t *testing.T) { m := io.NewMockMedium() m.Files["/state.json"] = "not valid json" @@ -97,7 +97,7 @@ func TestState_Load_Bad_InvalidJSON(t *testing.T) { assert.Error(t, err) } -func TestState_SaveLoad_Good_MultipleEntries(t *testing.T) { +func TestState_SaveLoad_Good_MultipleEntries_Good(t *testing.T) { m := io.NewMockMedium() s := NewState(m, "/state.json") @@ -125,7 +125,7 @@ func TestState_SaveLoad_Good_MultipleEntries(t *testing.T) { assert.Equal(t, 30, c.Items) } -func TestState_Set_Good_Overwrite(t *testing.T) { +func TestState_Set_Good_Overwrite_Good(t *testing.T) { m := io.NewMockMedium() s := NewState(m, "/state.json") diff --git a/forge/client.go b/forge/client.go index 6be8204..aa760ac 100644 --- a/forge/client.go +++ b/forge/client.go @@ -34,15 +34,19 @@ func New(url, token string) (*Client, error) { } // API exposes the underlying SDK client for direct access. +// Usage: API(...) func (c *Client) API() *forgejo.Client { return c.api } // URL returns the Forgejo instance URL. +// Usage: URL(...) func (c *Client) URL() string { return c.url } // Token returns the Forgejo API token. +// Usage: Token(...) func (c *Client) Token() string { return c.token } // GetCurrentUser returns the authenticated user's information. +// Usage: GetCurrentUser(...) func (c *Client) GetCurrentUser() (*forgejo.User, error) { user, _, err := c.api.GetMyUserInfo() if err != nil { @@ -52,6 +56,7 @@ func (c *Client) GetCurrentUser() (*forgejo.User, error) { } // ForkRepo forks a repository. If org is non-empty, forks into that organisation. +// Usage: ForkRepo(...) func (c *Client) ForkRepo(owner, repo string, org string) (*forgejo.Repository, error) { opts := forgejo.CreateForkOption{} if org != "" { @@ -66,6 +71,7 @@ func (c *Client) ForkRepo(owner, repo string, org string) (*forgejo.Repository, } // CreatePullRequest creates a pull request on the given repository. +// Usage: CreatePullRequest(...) func (c *Client) CreatePullRequest(owner, repo string, opts forgejo.CreatePullRequestOption) (*forgejo.PullRequest, error) { pr, _, err := c.api.CreatePullRequest(owner, repo, opts) if err != nil { diff --git a/forge/client_test.go b/forge/client_test.go index bb5b18a..e3dd182 100644 --- a/forge/client_test.go +++ b/forge/client_test.go @@ -27,7 +27,7 @@ func TestNew_Good(t *testing.T) { assert.Equal(t, "test-token-123", client.Token()) } -func TestNew_Bad_InvalidURL(t *testing.T) { +func TestNew_Bad_InvalidURL_Good(t *testing.T) { // The Forgejo SDK may reject certain URL formats. _, err := New("://invalid-url", "token") assert.Error(t, err) @@ -63,7 +63,7 @@ func TestClient_GetCurrentUser_Good(t *testing.T) { assert.Equal(t, "test-user", user.UserName) } -func TestClient_GetCurrentUser_Bad_ServerError(t *testing.T) { +func TestClient_GetCurrentUser_Bad_ServerError_Good(t *testing.T) { mux := http.NewServeMux() mux.HandleFunc("/api/v1/version", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") @@ -91,7 +91,7 @@ func TestClient_SetPRDraft_Good(t *testing.T) { require.NoError(t, err) } -func TestClient_SetPRDraft_Good_Undraft(t *testing.T) { +func TestClient_SetPRDraft_Good_Undraft_Good(t *testing.T) { client, srv := newTestClient(t) defer srv.Close() @@ -99,7 +99,7 @@ func TestClient_SetPRDraft_Good_Undraft(t *testing.T) { require.NoError(t, err) } -func TestClient_SetPRDraft_Bad_ServerError(t *testing.T) { +func TestClient_SetPRDraft_Bad_ServerError_Good(t *testing.T) { mux := http.NewServeMux() mux.HandleFunc("/api/v1/version", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") @@ -123,7 +123,7 @@ func TestClient_SetPRDraft_Bad_ServerError(t *testing.T) { assert.Contains(t, err.Error(), "unexpected status 403") } -func TestClient_SetPRDraft_Bad_ConnectionRefused(t *testing.T) { +func TestClient_SetPRDraft_Bad_ConnectionRefused_Good(t *testing.T) { // Use a closed server to simulate connection errors. srv := newMockForgejoServer(t) client, err := New(srv.URL, "token") @@ -134,7 +134,7 @@ func TestClient_SetPRDraft_Bad_ConnectionRefused(t *testing.T) { assert.Error(t, err) } -func TestClient_SetPRDraft_Good_URLConstruction(t *testing.T) { +func TestClient_SetPRDraft_Good_URLConstruction_Good(t *testing.T) { // Verify the URL is constructed correctly by checking the request path. var capturedPath string mux := http.NewServeMux() @@ -158,7 +158,7 @@ func TestClient_SetPRDraft_Good_URLConstruction(t *testing.T) { assert.Equal(t, "/api/v1/repos/my-org/my-repo/pulls/42", capturedPath) } -func TestClient_SetPRDraft_Good_AuthHeader(t *testing.T) { +func TestClient_SetPRDraft_Good_AuthHeader_Good(t *testing.T) { // Verify the authorisation header is set correctly. var capturedAuth string mux := http.NewServeMux() @@ -184,7 +184,7 @@ func TestClient_SetPRDraft_Good_AuthHeader(t *testing.T) { // --- PRMeta and Comment struct tests --- -func TestPRMeta_Good_Fields(t *testing.T) { +func TestPRMeta_Good_Fields_Good(t *testing.T) { meta := &PRMeta{ Number: 42, Title: "Test PR", @@ -210,7 +210,7 @@ func TestPRMeta_Good_Fields(t *testing.T) { assert.Equal(t, 5, meta.CommentCount) } -func TestComment_Good_Fields(t *testing.T) { +func TestComment_Good_Fields_Good(t *testing.T) { comment := Comment{ ID: 123, Author: "reviewer", @@ -224,7 +224,7 @@ func TestComment_Good_Fields(t *testing.T) { // --- MergePullRequest merge style mapping --- -func TestMergePullRequest_Good_StyleMapping(t *testing.T) { +func TestMergePullRequest_Good_StyleMapping_Good(t *testing.T) { // We can't easily test the SDK call, but we can verify the method // errors when the server returns failure. This exercises the style mapping code. tests := []struct { @@ -262,7 +262,7 @@ func TestMergePullRequest_Good_StyleMapping(t *testing.T) { // --- ListIssuesOpts defaulting --- -func TestListIssuesOpts_Good_Defaults(t *testing.T) { +func TestListIssuesOpts_Good_Defaults_Good(t *testing.T) { tests := []struct { name string opts ListIssuesOpts @@ -325,7 +325,7 @@ func TestListIssuesOpts_Good_Defaults(t *testing.T) { // --- ForkRepo error handling --- -func TestClient_ForkRepo_Good_WithOrg(t *testing.T) { +func TestClient_ForkRepo_Good_WithOrg_Good(t *testing.T) { mux := http.NewServeMux() mux.HandleFunc("/api/v1/version", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") @@ -355,7 +355,7 @@ func TestClient_ForkRepo_Good_WithOrg(t *testing.T) { assert.Equal(t, "target-org", capturedBody["organization"]) } -func TestClient_ForkRepo_Good_WithoutOrg(t *testing.T) { +func TestClient_ForkRepo_Good_WithoutOrg_Good(t *testing.T) { mux := http.NewServeMux() mux.HandleFunc("/api/v1/version", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") @@ -386,7 +386,7 @@ func TestClient_ForkRepo_Good_WithoutOrg(t *testing.T) { // The SDK may or may not include it in the JSON; just verify the fork succeeded. } -func TestClient_ForkRepo_Bad_ServerError(t *testing.T) { +func TestClient_ForkRepo_Bad_ServerError_Good(t *testing.T) { mux := http.NewServeMux() mux.HandleFunc("/api/v1/version", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") @@ -408,7 +408,7 @@ func TestClient_ForkRepo_Bad_ServerError(t *testing.T) { // --- CreatePullRequest error handling --- -func TestClient_CreatePullRequest_Bad_ServerError(t *testing.T) { +func TestClient_CreatePullRequest_Bad_ServerError_Good(t *testing.T) { mux := http.NewServeMux() mux.HandleFunc("/api/v1/version", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") @@ -440,7 +440,7 @@ func TestCommentPageSize_Good(t *testing.T) { // --- ListPullRequests state mapping --- -func TestListPullRequests_Good_StateMapping(t *testing.T) { +func TestListPullRequests_Good_StateMapping_Good(t *testing.T) { // Verify state mapping via error path (server returns error). tests := []struct { name string diff --git a/forge/config_test.go b/forge/config_test.go index 675728a..afb85ca 100644 --- a/forge/config_test.go +++ b/forge/config_test.go @@ -18,7 +18,7 @@ func isolateConfigEnv(t *testing.T) { t.Setenv("HOME", t.TempDir()) } -func TestResolveConfig_Good_Defaults(t *testing.T) { +func TestResolveConfig_Good_Defaults_Good(t *testing.T) { isolateConfigEnv(t) url, token, err := ResolveConfig("", "") @@ -27,7 +27,7 @@ func TestResolveConfig_Good_Defaults(t *testing.T) { assert.Empty(t, token, "token should be empty when nothing configured") } -func TestResolveConfig_Good_FlagsOverrideAll(t *testing.T) { +func TestResolveConfig_Good_FlagsOverrideAll_Good(t *testing.T) { isolateConfigEnv(t) t.Setenv("FORGE_URL", "https://env-url.example.com") t.Setenv("FORGE_TOKEN", "env-token-abc") @@ -38,7 +38,7 @@ func TestResolveConfig_Good_FlagsOverrideAll(t *testing.T) { assert.Equal(t, "flag-token-xyz", token, "flag token should override env") } -func TestResolveConfig_Good_EnvVarsOverrideConfig(t *testing.T) { +func TestResolveConfig_Good_EnvVarsOverrideConfig_Good(t *testing.T) { isolateConfigEnv(t) t.Setenv("FORGE_URL", "https://env-url.example.com") t.Setenv("FORGE_TOKEN", "env-token-123") @@ -49,7 +49,7 @@ func TestResolveConfig_Good_EnvVarsOverrideConfig(t *testing.T) { assert.Equal(t, "env-token-123", token) } -func TestResolveConfig_Good_PartialOverrides(t *testing.T) { +func TestResolveConfig_Good_PartialOverrides_Good(t *testing.T) { isolateConfigEnv(t) // Set only env URL, flag token. t.Setenv("FORGE_URL", "https://env-only.example.com") @@ -60,7 +60,7 @@ func TestResolveConfig_Good_PartialOverrides(t *testing.T) { assert.Equal(t, "flag-only-token", token, "flag token should be used") } -func TestResolveConfig_Good_URLDefaultsWhenEmpty(t *testing.T) { +func TestResolveConfig_Good_URLDefaultsWhenEmpty_Good(t *testing.T) { isolateConfigEnv(t) t.Setenv("FORGE_TOKEN", "some-token") @@ -76,7 +76,7 @@ func TestConstants_Good(t *testing.T) { assert.Equal(t, "http://localhost:4000", DefaultURL) } -func TestNewFromConfig_Bad_NoToken(t *testing.T) { +func TestNewFromConfig_Bad_NoToken_Good(t *testing.T) { isolateConfigEnv(t) _, err := NewFromConfig("", "") @@ -84,7 +84,7 @@ func TestNewFromConfig_Bad_NoToken(t *testing.T) { assert.Contains(t, err.Error(), "no API token configured") } -func TestNewFromConfig_Good_WithFlagToken(t *testing.T) { +func TestNewFromConfig_Good_WithFlagToken_Good(t *testing.T) { isolateConfigEnv(t) // The Forgejo SDK NewClient validates the token by calling /api/v1/version, @@ -99,7 +99,7 @@ func TestNewFromConfig_Good_WithFlagToken(t *testing.T) { assert.Equal(t, "test-token", client.Token()) } -func TestNewFromConfig_Good_EnvToken(t *testing.T) { +func TestNewFromConfig_Good_EnvToken_Good(t *testing.T) { isolateConfigEnv(t) srv := newMockForgejoServer(t) diff --git a/forge/issues_test.go b/forge/issues_test.go index 1038bce..8ac225f 100644 --- a/forge/issues_test.go +++ b/forge/issues_test.go @@ -21,7 +21,7 @@ func TestClient_ListIssues_Good(t *testing.T) { assert.Equal(t, "Issue 1", issues[0].Title) } -func TestClient_ListIssues_Good_StateMapping(t *testing.T) { +func TestClient_ListIssues_Good_StateMapping_Good(t *testing.T) { tests := []struct { name string state string @@ -43,7 +43,7 @@ func TestClient_ListIssues_Good_StateMapping(t *testing.T) { } } -func TestClient_ListIssues_Good_CustomPageAndLimit(t *testing.T) { +func TestClient_ListIssues_Good_CustomPageAndLimit_Good(t *testing.T) { client, srv := newTestClient(t) defer srv.Close() @@ -54,7 +54,7 @@ func TestClient_ListIssues_Good_CustomPageAndLimit(t *testing.T) { require.NoError(t, err) } -func TestClient_ListIssues_Bad_ServerError(t *testing.T) { +func TestClient_ListIssues_Bad_ServerError_Good(t *testing.T) { client, srv := newErrorServer(t) defer srv.Close() @@ -72,7 +72,7 @@ func TestClient_GetIssue_Good(t *testing.T) { assert.Equal(t, "Issue 1", issue.Title) } -func TestClient_GetIssue_Bad_ServerError(t *testing.T) { +func TestClient_GetIssue_Bad_ServerError_Good(t *testing.T) { client, srv := newErrorServer(t) defer srv.Close() @@ -93,7 +93,7 @@ func TestClient_CreateIssue_Good(t *testing.T) { assert.NotNil(t, issue) } -func TestClient_CreateIssue_Bad_ServerError(t *testing.T) { +func TestClient_CreateIssue_Bad_ServerError_Good(t *testing.T) { client, srv := newErrorServer(t) defer srv.Close() @@ -115,7 +115,7 @@ func TestClient_EditIssue_Good(t *testing.T) { assert.NotNil(t, issue) } -func TestClient_EditIssue_Bad_ServerError(t *testing.T) { +func TestClient_EditIssue_Bad_ServerError_Good(t *testing.T) { client, srv := newErrorServer(t) defer srv.Close() @@ -134,7 +134,7 @@ func TestClient_AssignIssue_Good(t *testing.T) { require.NoError(t, err) } -func TestClient_AssignIssue_Bad_ServerError(t *testing.T) { +func TestClient_AssignIssue_Bad_ServerError_Good(t *testing.T) { client, srv := newErrorServer(t) defer srv.Close() @@ -153,7 +153,7 @@ func TestClient_ListPullRequests_Good(t *testing.T) { assert.Equal(t, "PR 1", prs[0].Title) } -func TestClient_ListPullRequests_Good_StateMapping(t *testing.T) { +func TestClient_ListPullRequests_Good_StateMapping_Good(t *testing.T) { tests := []struct { name string state string @@ -175,7 +175,7 @@ func TestClient_ListPullRequests_Good_StateMapping(t *testing.T) { } } -func TestClient_ListPullRequests_Bad_ServerError(t *testing.T) { +func TestClient_ListPullRequests_Bad_ServerError_Good(t *testing.T) { client, srv := newErrorServer(t) defer srv.Close() @@ -193,7 +193,7 @@ func TestClient_GetPullRequest_Good(t *testing.T) { assert.Equal(t, "PR 1", pr.Title) } -func TestClient_GetPullRequest_Bad_ServerError(t *testing.T) { +func TestClient_GetPullRequest_Bad_ServerError_Good(t *testing.T) { client, srv := newErrorServer(t) defer srv.Close() @@ -210,7 +210,7 @@ func TestClient_CreateIssueComment_Good(t *testing.T) { require.NoError(t, err) } -func TestClient_CreateIssueComment_Bad_ServerError(t *testing.T) { +func TestClient_CreateIssueComment_Bad_ServerError_Good(t *testing.T) { client, srv := newErrorServer(t) defer srv.Close() @@ -229,7 +229,7 @@ func TestClient_ListIssueComments_Good(t *testing.T) { assert.Equal(t, "comment 1", comments[0].Body) } -func TestClient_ListIssueComments_Bad_ServerError(t *testing.T) { +func TestClient_ListIssueComments_Bad_ServerError_Good(t *testing.T) { client, srv := newErrorServer(t) defer srv.Close() @@ -246,7 +246,7 @@ func TestClient_CloseIssue_Good(t *testing.T) { require.NoError(t, err) } -func TestClient_CloseIssue_Bad_ServerError(t *testing.T) { +func TestClient_CloseIssue_Bad_ServerError_Good(t *testing.T) { client, srv := newErrorServer(t) defer srv.Close() diff --git a/forge/labels_test.go b/forge/labels_test.go index ca0ae1e..3e23bbe 100644 --- a/forge/labels_test.go +++ b/forge/labels_test.go @@ -26,7 +26,7 @@ func TestClient_ListRepoLabels_Good(t *testing.T) { assert.Equal(t, "feature", labels[1].Name) } -func TestClient_ListRepoLabels_Bad_ServerError(t *testing.T) { +func TestClient_ListRepoLabels_Bad_ServerError_Good(t *testing.T) { client, srv := newErrorServer(t) defer srv.Close() @@ -44,7 +44,7 @@ func TestClient_CreateRepoLabel_Good(t *testing.T) { assert.NotNil(t, label) } -func TestClient_CreateRepoLabel_Bad_ServerError(t *testing.T) { +func TestClient_CreateRepoLabel_Bad_ServerError_Good(t *testing.T) { client, srv := newErrorServer(t) defer srv.Close() @@ -62,7 +62,7 @@ func TestClient_GetLabelByName_Good(t *testing.T) { assert.Equal(t, "bug", label.Name) } -func TestClient_GetLabelByName_Good_CaseInsensitive(t *testing.T) { +func TestClient_GetLabelByName_Good_CaseInsensitive_Good(t *testing.T) { client, srv := newTestClient(t) defer srv.Close() @@ -71,7 +71,7 @@ func TestClient_GetLabelByName_Good_CaseInsensitive(t *testing.T) { assert.Equal(t, "bug", label.Name) } -func TestClient_GetLabelByName_Bad_NotFound(t *testing.T) { +func TestClient_GetLabelByName_Bad_NotFound_Good(t *testing.T) { client, srv := newTestClient(t) defer srv.Close() @@ -80,7 +80,7 @@ func TestClient_GetLabelByName_Bad_NotFound(t *testing.T) { assert.Contains(t, err.Error(), "label nonexistent not found") } -func TestClient_EnsureLabel_Good_Exists(t *testing.T) { +func TestClient_EnsureLabel_Good_Exists_Good(t *testing.T) { client, srv := newTestClient(t) defer srv.Close() @@ -90,7 +90,7 @@ func TestClient_EnsureLabel_Good_Exists(t *testing.T) { assert.Equal(t, "bug", label.Name) } -func TestClient_EnsureLabel_Good_Creates(t *testing.T) { +func TestClient_EnsureLabel_Good_Creates_Good(t *testing.T) { client, srv := newTestClient(t) defer srv.Close() @@ -110,7 +110,7 @@ func TestClient_ListOrgLabels_Good(t *testing.T) { assert.NotEmpty(t, labels) } -func TestClient_ListOrgLabels_Bad_ServerError(t *testing.T) { +func TestClient_ListOrgLabels_Bad_ServerError_Good(t *testing.T) { client, srv := newErrorServer(t) defer srv.Close() @@ -126,7 +126,7 @@ func TestClient_AddIssueLabels_Good(t *testing.T) { require.NoError(t, err) } -func TestClient_AddIssueLabels_Bad_ServerError(t *testing.T) { +func TestClient_AddIssueLabels_Bad_ServerError_Good(t *testing.T) { client, srv := newErrorServer(t) defer srv.Close() @@ -143,7 +143,7 @@ func TestClient_RemoveIssueLabel_Good(t *testing.T) { require.NoError(t, err) } -func TestClient_RemoveIssueLabel_Bad_ServerError(t *testing.T) { +func TestClient_RemoveIssueLabel_Bad_ServerError_Good(t *testing.T) { client, srv := newErrorServer(t) defer srv.Close() diff --git a/forge/meta.go b/forge/meta.go index 180b25f..4665f1d 100644 --- a/forge/meta.go +++ b/forge/meta.go @@ -40,6 +40,7 @@ const commentPageSize = 50 // GetPRMeta returns structural signals for a pull request. // This is the Forgejo side of the dual MetaReader described in the pipeline design. +// Usage: GetPRMeta(...) func (c *Client) GetPRMeta(owner, repo string, pr int64) (*PRMeta, error) { pull, _, err := c.api.GetPullRequest(owner, repo, pr) if err != nil { @@ -97,6 +98,7 @@ func (c *Client) GetPRMeta(owner, repo string, pr int64) (*PRMeta, error) { } // GetCommentBodies returns all comment bodies for a pull request. +// Usage: GetCommentBodies(...) func (c *Client) GetCommentBodies(owner, repo string, pr int64) ([]Comment, error) { var comments []Comment page := 1 @@ -136,6 +138,7 @@ func (c *Client) GetCommentBodies(owner, repo string, pr int64) ([]Comment, erro } // GetIssueBody returns the body text of an issue. +// Usage: GetIssueBody(...) func (c *Client) GetIssueBody(owner, repo string, issue int64) (string, error) { iss, _, err := c.api.GetIssue(owner, repo, issue) if err != nil { diff --git a/forge/meta_test.go b/forge/meta_test.go index 34247e9..a209f7f 100644 --- a/forge/meta_test.go +++ b/forge/meta_test.go @@ -25,7 +25,7 @@ func TestClient_GetPRMeta_Good(t *testing.T) { assert.False(t, meta.IsMerged) } -func TestClient_GetPRMeta_Bad_ServerError(t *testing.T) { +func TestClient_GetPRMeta_Bad_ServerError_Good(t *testing.T) { client, srv := newErrorServer(t) defer srv.Close() @@ -47,7 +47,7 @@ func TestClient_GetCommentBodies_Good(t *testing.T) { assert.Equal(t, "user2", comments[1].Author) } -func TestClient_GetCommentBodies_Bad_ServerError(t *testing.T) { +func TestClient_GetCommentBodies_Bad_ServerError_Good(t *testing.T) { client, srv := newErrorServer(t) defer srv.Close() @@ -65,7 +65,7 @@ func TestClient_GetIssueBody_Good(t *testing.T) { assert.Equal(t, "First issue body", body) } -func TestClient_GetIssueBody_Bad_ServerError(t *testing.T) { +func TestClient_GetIssueBody_Bad_ServerError_Good(t *testing.T) { client, srv := newErrorServer(t) defer srv.Close() diff --git a/forge/orgs_test.go b/forge/orgs_test.go index efd3951..c93bfb2 100644 --- a/forge/orgs_test.go +++ b/forge/orgs_test.go @@ -21,7 +21,7 @@ func TestClient_ListMyOrgs_Good(t *testing.T) { assert.Equal(t, "test-org", orgs[0].UserName) } -func TestClient_ListMyOrgs_Bad_ServerError(t *testing.T) { +func TestClient_ListMyOrgs_Bad_ServerError_Good(t *testing.T) { client, srv := newErrorServer(t) defer srv.Close() @@ -39,7 +39,7 @@ func TestClient_GetOrg_Good(t *testing.T) { assert.Equal(t, "test-org", org.UserName) } -func TestClient_GetOrg_Bad_ServerError(t *testing.T) { +func TestClient_GetOrg_Bad_ServerError_Good(t *testing.T) { client, srv := newErrorServer(t) defer srv.Close() @@ -61,7 +61,7 @@ func TestClient_CreateOrg_Good(t *testing.T) { assert.NotNil(t, org) } -func TestClient_CreateOrg_Bad_ServerError(t *testing.T) { +func TestClient_CreateOrg_Bad_ServerError_Good(t *testing.T) { client, srv := newErrorServer(t) defer srv.Close() diff --git a/forge/prs_test.go b/forge/prs_test.go index 449aed2..80c992b 100644 --- a/forge/prs_test.go +++ b/forge/prs_test.go @@ -21,7 +21,7 @@ func TestClient_MergePullRequest_Good(t *testing.T) { require.NoError(t, err) } -func TestClient_MergePullRequest_Good_Squash(t *testing.T) { +func TestClient_MergePullRequest_Good_Squash_Good(t *testing.T) { client, srv := newTestClient(t) defer srv.Close() @@ -29,7 +29,7 @@ func TestClient_MergePullRequest_Good_Squash(t *testing.T) { require.NoError(t, err) } -func TestClient_MergePullRequest_Good_Rebase(t *testing.T) { +func TestClient_MergePullRequest_Good_Rebase_Good(t *testing.T) { client, srv := newTestClient(t) defer srv.Close() @@ -37,7 +37,7 @@ func TestClient_MergePullRequest_Good_Rebase(t *testing.T) { require.NoError(t, err) } -func TestClient_MergePullRequest_Bad_ServerError(t *testing.T) { +func TestClient_MergePullRequest_Bad_ServerError_Good(t *testing.T) { client, srv := newErrorServer(t) defer srv.Close() @@ -60,7 +60,7 @@ func TestClient_ListPRReviews_Good(t *testing.T) { require.Len(t, reviews, 1) } -func TestClient_ListPRReviews_Bad_ServerError(t *testing.T) { +func TestClient_ListPRReviews_Bad_ServerError_Good(t *testing.T) { client, srv := newErrorServer(t) defer srv.Close() @@ -78,7 +78,7 @@ func TestClient_GetCombinedStatus_Good(t *testing.T) { assert.NotNil(t, status) } -func TestClient_GetCombinedStatus_Bad_ServerError(t *testing.T) { +func TestClient_GetCombinedStatus_Bad_ServerError_Good(t *testing.T) { client, srv := newErrorServer(t) defer srv.Close() @@ -95,7 +95,7 @@ func TestClient_DismissReview_Good(t *testing.T) { require.NoError(t, err) } -func TestClient_DismissReview_Bad_ServerError(t *testing.T) { +func TestClient_DismissReview_Bad_ServerError_Good(t *testing.T) { client, srv := newErrorServer(t) defer srv.Close() @@ -104,7 +104,7 @@ func TestClient_DismissReview_Bad_ServerError(t *testing.T) { assert.Contains(t, err.Error(), "failed to dismiss review") } -func TestClient_SetPRDraft_Good_Request(t *testing.T) { +func TestClient_SetPRDraft_Good_Request_Good(t *testing.T) { var method, path string var payload map[string]any @@ -132,7 +132,7 @@ func TestClient_SetPRDraft_Good_Request(t *testing.T) { assert.Equal(t, false, payload["draft"]) } -func TestClient_SetPRDraft_Bad_PathTraversalOwner(t *testing.T) { +func TestClient_SetPRDraft_Bad_PathTraversalOwner_Good(t *testing.T) { client, srv := newTestClient(t) defer srv.Close() @@ -141,7 +141,7 @@ func TestClient_SetPRDraft_Bad_PathTraversalOwner(t *testing.T) { assert.Contains(t, err.Error(), "invalid owner") } -func TestClient_SetPRDraft_Bad_PathTraversalRepo(t *testing.T) { +func TestClient_SetPRDraft_Bad_PathTraversalRepo_Good(t *testing.T) { client, srv := newTestClient(t) defer srv.Close() diff --git a/forge/repos_test.go b/forge/repos_test.go index 7318730..c3f1792 100644 --- a/forge/repos_test.go +++ b/forge/repos_test.go @@ -21,7 +21,7 @@ func TestClient_ListOrgRepos_Good(t *testing.T) { assert.Equal(t, "org-repo", repos[0].Name) } -func TestClient_ListOrgRepos_Bad_ServerError(t *testing.T) { +func TestClient_ListOrgRepos_Bad_ServerError_Good(t *testing.T) { client, srv := newErrorServer(t) defer srv.Close() @@ -41,7 +41,7 @@ func TestClient_ListUserRepos_Good(t *testing.T) { assert.Equal(t, "repo-b", repos[1].Name) } -func TestClient_ListUserRepos_Bad_ServerError(t *testing.T) { +func TestClient_ListUserRepos_Bad_ServerError_Good(t *testing.T) { client, srv := newErrorServer(t) defer srv.Close() @@ -59,7 +59,7 @@ func TestClient_GetRepo_Good(t *testing.T) { assert.Equal(t, "org-repo", repo.Name) } -func TestClient_GetRepo_Bad_ServerError(t *testing.T) { +func TestClient_GetRepo_Bad_ServerError_Good(t *testing.T) { client, srv := newErrorServer(t) defer srv.Close() @@ -80,7 +80,7 @@ func TestClient_CreateOrgRepo_Good(t *testing.T) { assert.NotNil(t, repo) } -func TestClient_CreateOrgRepo_Bad_ServerError(t *testing.T) { +func TestClient_CreateOrgRepo_Bad_ServerError_Good(t *testing.T) { client, srv := newErrorServer(t) defer srv.Close() @@ -99,7 +99,7 @@ func TestClient_DeleteRepo_Good(t *testing.T) { require.NoError(t, err) } -func TestClient_DeleteRepo_Bad_ServerError(t *testing.T) { +func TestClient_DeleteRepo_Bad_ServerError_Good(t *testing.T) { client, srv := newErrorServer(t) defer srv.Close() @@ -121,7 +121,7 @@ func TestClient_MigrateRepo_Good(t *testing.T) { assert.NotNil(t, repo) } -func TestClient_MigrateRepo_Bad_ServerError(t *testing.T) { +func TestClient_MigrateRepo_Bad_ServerError_Good(t *testing.T) { client, srv := newErrorServer(t) defer srv.Close() diff --git a/forge/webhooks_test.go b/forge/webhooks_test.go index 62a7709..c0f34aa 100644 --- a/forge/webhooks_test.go +++ b/forge/webhooks_test.go @@ -25,7 +25,7 @@ func TestClient_CreateRepoWebhook_Good(t *testing.T) { assert.NotNil(t, hook) } -func TestClient_CreateRepoWebhook_Bad_ServerError(t *testing.T) { +func TestClient_CreateRepoWebhook_Bad_ServerError_Good(t *testing.T) { client, srv := newErrorServer(t) defer srv.Close() @@ -45,7 +45,7 @@ func TestClient_ListRepoWebhooks_Good(t *testing.T) { require.Len(t, hooks, 1) } -func TestClient_ListRepoWebhooks_Bad_ServerError(t *testing.T) { +func TestClient_ListRepoWebhooks_Bad_ServerError_Good(t *testing.T) { client, srv := newErrorServer(t) defer srv.Close() diff --git a/gitea/client.go b/gitea/client.go index 4bb9581..db89702 100644 --- a/gitea/client.go +++ b/gitea/client.go @@ -33,7 +33,9 @@ func New(url, token string) (*Client, error) { } // API exposes the underlying SDK client for direct access. +// Usage: API(...) func (c *Client) API() *gitea.Client { return c.api } // URL returns the Gitea instance URL. +// Usage: URL(...) func (c *Client) URL() string { return c.url } diff --git a/gitea/client_test.go b/gitea/client_test.go index 49f1934..3f9f8c3 100644 --- a/gitea/client_test.go +++ b/gitea/client_test.go @@ -20,7 +20,7 @@ func TestNew_Good(t *testing.T) { assert.Equal(t, srv.URL, client.URL()) } -func TestNew_Bad_InvalidURL(t *testing.T) { +func TestNew_Bad_InvalidURL_Good(t *testing.T) { _, err := New("://invalid-url", "token") assert.Error(t, err) } diff --git a/gitea/config_test.go b/gitea/config_test.go index 6b6c571..e8d554d 100644 --- a/gitea/config_test.go +++ b/gitea/config_test.go @@ -17,7 +17,7 @@ func isolateConfigEnv(t *testing.T) { t.Setenv("HOME", t.TempDir()) } -func TestResolveConfig_Good_Defaults(t *testing.T) { +func TestResolveConfig_Good_Defaults_Good(t *testing.T) { isolateConfigEnv(t) url, token, err := ResolveConfig("", "") @@ -26,7 +26,7 @@ func TestResolveConfig_Good_Defaults(t *testing.T) { assert.Empty(t, token, "token should be empty when nothing configured") } -func TestResolveConfig_Good_FlagsOverrideAll(t *testing.T) { +func TestResolveConfig_Good_FlagsOverrideAll_Good(t *testing.T) { isolateConfigEnv(t) t.Setenv("GITEA_URL", "https://env-url.example.com") t.Setenv("GITEA_TOKEN", "env-token-abc") @@ -37,7 +37,7 @@ func TestResolveConfig_Good_FlagsOverrideAll(t *testing.T) { assert.Equal(t, "flag-token-xyz", token, "flag token should override env") } -func TestResolveConfig_Good_EnvVarsOverrideConfig(t *testing.T) { +func TestResolveConfig_Good_EnvVarsOverrideConfig_Good(t *testing.T) { isolateConfigEnv(t) t.Setenv("GITEA_URL", "https://env-url.example.com") t.Setenv("GITEA_TOKEN", "env-token-123") @@ -48,7 +48,7 @@ func TestResolveConfig_Good_EnvVarsOverrideConfig(t *testing.T) { assert.Equal(t, "env-token-123", token) } -func TestResolveConfig_Good_PartialOverrides(t *testing.T) { +func TestResolveConfig_Good_PartialOverrides_Good(t *testing.T) { isolateConfigEnv(t) t.Setenv("GITEA_URL", "https://env-only.example.com") @@ -58,7 +58,7 @@ func TestResolveConfig_Good_PartialOverrides(t *testing.T) { assert.Equal(t, "flag-only-token", token, "flag token should be used") } -func TestResolveConfig_Good_URLDefaultsWhenEmpty(t *testing.T) { +func TestResolveConfig_Good_URLDefaultsWhenEmpty_Good(t *testing.T) { isolateConfigEnv(t) t.Setenv("GITEA_TOKEN", "some-token") @@ -74,7 +74,7 @@ func TestConstants_Good(t *testing.T) { assert.Equal(t, "https://gitea.snider.dev", DefaultURL) } -func TestNewFromConfig_Bad_NoToken(t *testing.T) { +func TestNewFromConfig_Bad_NoToken_Good(t *testing.T) { isolateConfigEnv(t) _, err := NewFromConfig("", "") @@ -82,7 +82,7 @@ func TestNewFromConfig_Bad_NoToken(t *testing.T) { assert.Contains(t, err.Error(), "no API token configured") } -func TestNewFromConfig_Good_WithFlagToken(t *testing.T) { +func TestNewFromConfig_Good_WithFlagToken_Good(t *testing.T) { isolateConfigEnv(t) srv := newMockGiteaServer(t) @@ -94,7 +94,7 @@ func TestNewFromConfig_Good_WithFlagToken(t *testing.T) { assert.Equal(t, srv.URL, client.URL()) } -func TestNewFromConfig_Good_EnvToken(t *testing.T) { +func TestNewFromConfig_Good_EnvToken_Good(t *testing.T) { isolateConfigEnv(t) srv := newMockGiteaServer(t) diff --git a/gitea/coverage_boost_test.go b/gitea/coverage_boost_test.go index ccf6ebc..c12f70b 100644 --- a/gitea/coverage_boost_test.go +++ b/gitea/coverage_boost_test.go @@ -14,7 +14,7 @@ import ( // --- SaveConfig tests --- -func TestSaveConfig_Good_URLAndToken(t *testing.T) { +func TestSaveConfig_Good_URLAndToken_Good(t *testing.T) { isolateConfigEnv(t) err := SaveConfig("https://gitea.example.com", "test-token-123") @@ -25,7 +25,7 @@ func TestSaveConfig_Good_URLAndToken(t *testing.T) { } } -func TestSaveConfig_Good_URLOnly(t *testing.T) { +func TestSaveConfig_Good_URLOnly_Good(t *testing.T) { isolateConfigEnv(t) err := SaveConfig("https://gitea.example.com", "") @@ -34,7 +34,7 @@ func TestSaveConfig_Good_URLOnly(t *testing.T) { } } -func TestSaveConfig_Good_TokenOnly(t *testing.T) { +func TestSaveConfig_Good_TokenOnly_Good(t *testing.T) { isolateConfigEnv(t) err := SaveConfig("", "some-token") @@ -43,7 +43,7 @@ func TestSaveConfig_Good_TokenOnly(t *testing.T) { } } -func TestSaveConfig_Good_Empty(t *testing.T) { +func TestSaveConfig_Good_Empty_Good(t *testing.T) { isolateConfigEnv(t) err := SaveConfig("", "") @@ -89,7 +89,7 @@ func newPaginatedOrgReposServer(t *testing.T) *httptest.Server { return httptest.NewServer(mux) } -func TestClient_ListOrgRepos_Good_Pagination(t *testing.T) { +func TestClient_ListOrgRepos_Good_Pagination_Good(t *testing.T) { srv := newPaginatedOrgReposServer(t) defer srv.Close() @@ -123,7 +123,7 @@ func newPaginatedUserReposServer(t *testing.T) *httptest.Server { return httptest.NewServer(mux) } -func TestClient_ListUserRepos_Good_SinglePage(t *testing.T) { +func TestClient_ListUserRepos_Good_SinglePage_Good(t *testing.T) { srv := newPaginatedUserReposServer(t) defer srv.Close() @@ -175,7 +175,7 @@ func newPRMetaWithManyCommentsServer(t *testing.T) *httptest.Server { return httptest.NewServer(mux) } -func TestClient_GetPRMeta_Good_CommentCount(t *testing.T) { +func TestClient_GetPRMeta_Good_CommentCount_Good(t *testing.T) { srv := newPRMetaWithManyCommentsServer(t) defer srv.Close() @@ -219,7 +219,7 @@ func newPRMetaWithNilDatesServer(t *testing.T) *httptest.Server { return httptest.NewServer(mux) } -func TestClient_GetPRMeta_Good_MinimalFields(t *testing.T) { +func TestClient_GetPRMeta_Good_MinimalFields_Good(t *testing.T) { srv := newPRMetaWithNilDatesServer(t) defer srv.Close() @@ -238,7 +238,7 @@ func TestClient_GetPRMeta_Good_MinimalFields(t *testing.T) { // --- GetCommentBodies: empty result --- -func TestClient_GetCommentBodies_Good_Empty(t *testing.T) { +func TestClient_GetCommentBodies_Good_Empty_Good(t *testing.T) { mux := http.NewServeMux() mux.HandleFunc("/api/v1/version", func(w http.ResponseWriter, r *http.Request) { jsonResponse(w, map[string]string{"version": "1.21.0"}) @@ -263,7 +263,7 @@ func TestClient_GetCommentBodies_Good_Empty(t *testing.T) { // --- GetCommentBodies: poster is nil --- -func TestClient_GetCommentBodies_Good_NilPoster(t *testing.T) { +func TestClient_GetCommentBodies_Good_NilPoster_Good(t *testing.T) { mux := http.NewServeMux() mux.HandleFunc("/api/v1/version", func(w http.ResponseWriter, r *http.Request) { jsonResponse(w, map[string]string{"version": "1.21.0"}) @@ -293,7 +293,7 @@ func TestClient_GetCommentBodies_Good_NilPoster(t *testing.T) { // --- ListPullRequests: state mapping --- -func TestClient_ListPullRequests_Good_AllStates(t *testing.T) { +func TestClient_ListPullRequests_Good_AllStates_Good(t *testing.T) { client, srv := newTestClient(t) defer srv.Close() @@ -305,7 +305,7 @@ func TestClient_ListPullRequests_Good_AllStates(t *testing.T) { // --- NewFromConfig: additional paths --- -func TestNewFromConfig_Good_FlagOverridesEnv(t *testing.T) { +func TestNewFromConfig_Good_FlagOverridesEnv_Good(t *testing.T) { isolateConfigEnv(t) srv := newMockGiteaServer(t) diff --git a/gitea/issues_test.go b/gitea/issues_test.go index 6012eea..5708740 100644 --- a/gitea/issues_test.go +++ b/gitea/issues_test.go @@ -21,7 +21,7 @@ func TestClient_ListIssues_Good(t *testing.T) { assert.Equal(t, "Issue 1", issues[0].Title) } -func TestClient_ListIssues_Good_StateMapping(t *testing.T) { +func TestClient_ListIssues_Good_StateMapping_Good(t *testing.T) { tests := []struct { name string state string @@ -43,7 +43,7 @@ func TestClient_ListIssues_Good_StateMapping(t *testing.T) { } } -func TestClient_ListIssues_Good_CustomPageAndLimit(t *testing.T) { +func TestClient_ListIssues_Good_CustomPageAndLimit_Good(t *testing.T) { client, srv := newTestClient(t) defer srv.Close() @@ -54,7 +54,7 @@ func TestClient_ListIssues_Good_CustomPageAndLimit(t *testing.T) { require.NoError(t, err) } -func TestClient_ListIssues_Bad_ServerError(t *testing.T) { +func TestClient_ListIssues_Bad_ServerError_Good(t *testing.T) { client, srv := newErrorServer(t) defer srv.Close() @@ -72,7 +72,7 @@ func TestClient_GetIssue_Good(t *testing.T) { assert.Equal(t, "Issue 1", issue.Title) } -func TestClient_GetIssue_Bad_ServerError(t *testing.T) { +func TestClient_GetIssue_Bad_ServerError_Good(t *testing.T) { client, srv := newErrorServer(t) defer srv.Close() @@ -93,7 +93,7 @@ func TestClient_CreateIssue_Good(t *testing.T) { assert.NotNil(t, issue) } -func TestClient_CreateIssue_Bad_ServerError(t *testing.T) { +func TestClient_CreateIssue_Bad_ServerError_Good(t *testing.T) { client, srv := newErrorServer(t) defer srv.Close() @@ -114,7 +114,7 @@ func TestClient_ListPullRequests_Good(t *testing.T) { assert.Equal(t, "PR 1", prs[0].Title) } -func TestClient_ListPullRequests_Good_StateMapping(t *testing.T) { +func TestClient_ListPullRequests_Good_StateMapping_Good(t *testing.T) { tests := []struct { name string state string @@ -136,7 +136,7 @@ func TestClient_ListPullRequests_Good_StateMapping(t *testing.T) { } } -func TestClient_ListPullRequests_Bad_ServerError(t *testing.T) { +func TestClient_ListPullRequests_Bad_ServerError_Good(t *testing.T) { client, srv := newErrorServer(t) defer srv.Close() @@ -154,7 +154,7 @@ func TestClient_GetPullRequest_Good(t *testing.T) { assert.Equal(t, "PR 1", pr.Title) } -func TestClient_GetPullRequest_Bad_ServerError(t *testing.T) { +func TestClient_GetPullRequest_Bad_ServerError_Good(t *testing.T) { client, srv := newErrorServer(t) defer srv.Close() @@ -165,7 +165,7 @@ func TestClient_GetPullRequest_Bad_ServerError(t *testing.T) { // --- ListIssuesOpts defaulting --- -func TestListIssuesOpts_Good_Defaults(t *testing.T) { +func TestListIssuesOpts_Good_Defaults_Good(t *testing.T) { tests := []struct { name string opts ListIssuesOpts diff --git a/gitea/meta.go b/gitea/meta.go index 67ddd7e..30c4891 100644 --- a/gitea/meta.go +++ b/gitea/meta.go @@ -40,6 +40,7 @@ const commentPageSize = 50 // GetPRMeta returns structural signals for a pull request. // This is the Gitea side of the dual MetaReader described in the pipeline design. +// Usage: GetPRMeta(...) func (c *Client) GetPRMeta(owner, repo string, pr int64) (*PRMeta, error) { pull, _, err := c.api.GetPullRequest(owner, repo, pr) if err != nil { @@ -98,6 +99,7 @@ func (c *Client) GetPRMeta(owner, repo string, pr int64) (*PRMeta, error) { // GetCommentBodies returns all comment bodies for a pull request. // This reads full content, which is safe on the home lab Gitea instance. +// Usage: GetCommentBodies(...) func (c *Client) GetCommentBodies(owner, repo string, pr int64) ([]Comment, error) { var comments []Comment page := 1 @@ -138,6 +140,7 @@ func (c *Client) GetCommentBodies(owner, repo string, pr int64) ([]Comment, erro // GetIssueBody returns the body text of an issue. // This reads full content, which is safe on the home lab Gitea instance. +// Usage: GetIssueBody(...) func (c *Client) GetIssueBody(owner, repo string, issue int64) (string, error) { iss, _, err := c.api.GetIssue(owner, repo, issue) if err != nil { diff --git a/gitea/meta_test.go b/gitea/meta_test.go index f0cbc0a..c7fc733 100644 --- a/gitea/meta_test.go +++ b/gitea/meta_test.go @@ -25,7 +25,7 @@ func TestClient_GetPRMeta_Good(t *testing.T) { assert.False(t, meta.IsMerged) } -func TestClient_GetPRMeta_Bad_ServerError(t *testing.T) { +func TestClient_GetPRMeta_Bad_ServerError_Good(t *testing.T) { client, srv := newErrorServer(t) defer srv.Close() @@ -47,7 +47,7 @@ func TestClient_GetCommentBodies_Good(t *testing.T) { assert.Equal(t, "user2", comments[1].Author) } -func TestClient_GetCommentBodies_Bad_ServerError(t *testing.T) { +func TestClient_GetCommentBodies_Bad_ServerError_Good(t *testing.T) { client, srv := newErrorServer(t) defer srv.Close() @@ -65,7 +65,7 @@ func TestClient_GetIssueBody_Good(t *testing.T) { assert.Equal(t, "First issue body", body) } -func TestClient_GetIssueBody_Bad_ServerError(t *testing.T) { +func TestClient_GetIssueBody_Bad_ServerError_Good(t *testing.T) { client, srv := newErrorServer(t) defer srv.Close() @@ -76,7 +76,7 @@ func TestClient_GetIssueBody_Bad_ServerError(t *testing.T) { // --- PRMeta struct tests --- -func TestPRMeta_Good_Fields(t *testing.T) { +func TestPRMeta_Good_Fields_Good(t *testing.T) { meta := &PRMeta{ Number: 42, Title: "Test PR", @@ -102,7 +102,7 @@ func TestPRMeta_Good_Fields(t *testing.T) { assert.Equal(t, 5, meta.CommentCount) } -func TestComment_Good_Fields(t *testing.T) { +func TestComment_Good_Fields_Good(t *testing.T) { comment := Comment{ ID: 123, Author: "reviewer", diff --git a/gitea/repos_test.go b/gitea/repos_test.go index e23dc84..4ae97ba 100644 --- a/gitea/repos_test.go +++ b/gitea/repos_test.go @@ -21,7 +21,7 @@ func TestClient_ListOrgRepos_Good(t *testing.T) { assert.Equal(t, "org-repo", repos[0].Name) } -func TestClient_ListOrgRepos_Bad_ServerError(t *testing.T) { +func TestClient_ListOrgRepos_Bad_ServerError_Good(t *testing.T) { client, srv := newErrorServer(t) defer srv.Close() @@ -41,7 +41,7 @@ func TestClient_ListUserRepos_Good(t *testing.T) { assert.Equal(t, "repo-b", repos[1].Name) } -func TestClient_ListUserRepos_Bad_ServerError(t *testing.T) { +func TestClient_ListUserRepos_Bad_ServerError_Good(t *testing.T) { client, srv := newErrorServer(t) defer srv.Close() @@ -59,7 +59,7 @@ func TestClient_GetRepo_Good(t *testing.T) { assert.Equal(t, "org-repo", repo.Name) } -func TestClient_GetRepo_Bad_ServerError(t *testing.T) { +func TestClient_GetRepo_Bad_ServerError_Good(t *testing.T) { client, srv := newErrorServer(t) defer srv.Close() @@ -68,7 +68,7 @@ func TestClient_GetRepo_Bad_ServerError(t *testing.T) { assert.Contains(t, err.Error(), "failed to get repo") } -func TestClient_CreateMirror_Good_WithAuth(t *testing.T) { +func TestClient_CreateMirror_Good_WithAuth_Good(t *testing.T) { client, srv := newTestClient(t) defer srv.Close() @@ -78,7 +78,7 @@ func TestClient_CreateMirror_Good_WithAuth(t *testing.T) { assert.NotNil(t, repo) } -func TestClient_CreateMirror_Bad_NoAuthToken(t *testing.T) { +func TestClient_CreateMirror_Bad_NoAuthToken_Good(t *testing.T) { client, srv := newTestClient(t) defer srv.Close() @@ -88,7 +88,7 @@ func TestClient_CreateMirror_Bad_NoAuthToken(t *testing.T) { assert.Contains(t, err.Error(), "failed to create mirror") } -func TestClient_CreateMirror_Bad_ServerError(t *testing.T) { +func TestClient_CreateMirror_Bad_ServerError_Good(t *testing.T) { client, srv := newErrorServer(t) defer srv.Close() @@ -105,7 +105,7 @@ func TestClient_DeleteRepo_Good(t *testing.T) { require.NoError(t, err) } -func TestClient_DeleteRepo_Bad_ServerError(t *testing.T) { +func TestClient_DeleteRepo_Bad_ServerError_Good(t *testing.T) { client, srv := newErrorServer(t) defer srv.Close() @@ -126,7 +126,7 @@ func TestClient_CreateOrgRepo_Good(t *testing.T) { assert.NotNil(t, repo) } -func TestClient_CreateOrgRepo_Bad_ServerError(t *testing.T) { +func TestClient_CreateOrgRepo_Bad_ServerError_Good(t *testing.T) { client, srv := newErrorServer(t) defer srv.Close() diff --git a/jobrunner/forgejo/source_test.go b/jobrunner/forgejo/source_test.go index 83078b3..55d844e 100644 --- a/jobrunner/forgejo/source_test.go +++ b/jobrunner/forgejo/source_test.go @@ -37,7 +37,7 @@ func newTestClient(t *testing.T, url string) *forge.Client { return client } -func TestForgejoSource_Good_Name(t *testing.T) { +func TestForgejoSource_Good_Name_Good(t *testing.T) { s := New(Config{}, nil) assert.Equal(t, "forgejo", s.Name()) } @@ -108,7 +108,7 @@ func TestForgejoSource_Poll_Good(t *testing.T) { assert.Equal(t, "abc123", sig.LastCommitSHA) } -func TestForgejoSource_Poll_Good_NoEpics(t *testing.T) { +func TestForgejoSource_Poll_Good_NoEpics_Good(t *testing.T) { srv := httptest.NewServer(withVersion(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") _ = json.NewEncoder(w).Encode([]any{}) diff --git a/jobrunner/handlers/completion.go b/jobrunner/handlers/completion.go index e4e71fd..5beeca7 100644 --- a/jobrunner/handlers/completion.go +++ b/jobrunner/handlers/completion.go @@ -30,16 +30,19 @@ func NewCompletionHandler(client *forge.Client) *CompletionHandler { } // Name returns the handler identifier. +// Usage: Name(...) func (h *CompletionHandler) Name() string { return "completion" } // Match returns true if the signal indicates an agent has finished a task. +// Usage: Match(...) func (h *CompletionHandler) Match(signal *jobrunner.PipelineSignal) bool { return signal.Type == "agent_completion" } // Execute updates the issue labels based on the completion status. +// Usage: Execute(...) func (h *CompletionHandler) Execute(ctx context.Context, signal *jobrunner.PipelineSignal) (*jobrunner.ActionResult, error) { start := time.Now() diff --git a/jobrunner/handlers/dispatch_test.go b/jobrunner/handlers/dispatch_test.go index 9a348b8..9c4a9a5 100644 --- a/jobrunner/handlers/dispatch_test.go +++ b/jobrunner/handlers/dispatch_test.go @@ -37,7 +37,7 @@ func newTestSpinner(agents map[string]agentci.AgentConfig) *agentci.Spinner { // --- Match tests --- -func TestDispatch_Match_Good_NeedsCoding(t *testing.T) { +func TestDispatch_Match_Good_NeedsCoding_Good(t *testing.T) { spinner := newTestSpinner(map[string]agentci.AgentConfig{ "darbs-claude": {Host: "claude@192.168.0.201", QueueDir: "~/ai-work/queue", Active: true}, }) @@ -49,7 +49,7 @@ func TestDispatch_Match_Good_NeedsCoding(t *testing.T) { assert.True(t, h.Match(sig)) } -func TestDispatch_Match_Good_MultipleAgents(t *testing.T) { +func TestDispatch_Match_Good_MultipleAgents_Good(t *testing.T) { spinner := newTestSpinner(map[string]agentci.AgentConfig{ "darbs-claude": {Host: "claude@192.168.0.201", QueueDir: "~/ai-work/queue", Active: true}, "local-codex": {Host: "localhost", QueueDir: "~/ai-work/queue", Active: true}, @@ -62,7 +62,7 @@ func TestDispatch_Match_Good_MultipleAgents(t *testing.T) { assert.True(t, h.Match(sig)) } -func TestDispatch_Match_Bad_HasPR(t *testing.T) { +func TestDispatch_Match_Bad_HasPR_Good(t *testing.T) { spinner := newTestSpinner(map[string]agentci.AgentConfig{ "darbs-claude": {Host: "claude@192.168.0.201", QueueDir: "~/ai-work/queue", Active: true}, }) @@ -75,7 +75,7 @@ func TestDispatch_Match_Bad_HasPR(t *testing.T) { assert.False(t, h.Match(sig)) } -func TestDispatch_Match_Bad_UnknownAgent(t *testing.T) { +func TestDispatch_Match_Bad_UnknownAgent_Good(t *testing.T) { spinner := newTestSpinner(map[string]agentci.AgentConfig{ "darbs-claude": {Host: "claude@192.168.0.201", QueueDir: "~/ai-work/queue", Active: true}, }) @@ -87,7 +87,7 @@ func TestDispatch_Match_Bad_UnknownAgent(t *testing.T) { assert.False(t, h.Match(sig)) } -func TestDispatch_Match_Bad_NotAssigned(t *testing.T) { +func TestDispatch_Match_Bad_NotAssigned_Good(t *testing.T) { spinner := newTestSpinner(map[string]agentci.AgentConfig{ "darbs-claude": {Host: "claude@192.168.0.201", QueueDir: "~/ai-work/queue", Active: true}, }) @@ -99,7 +99,7 @@ func TestDispatch_Match_Bad_NotAssigned(t *testing.T) { assert.False(t, h.Match(sig)) } -func TestDispatch_Match_Bad_EmptyAgentMap(t *testing.T) { +func TestDispatch_Match_Bad_EmptyAgentMap_Good(t *testing.T) { spinner := newTestSpinner(map[string]agentci.AgentConfig{}) h := NewDispatchHandler(nil, "", "", spinner) sig := &jobrunner.PipelineSignal{ @@ -119,7 +119,7 @@ func TestDispatch_Name_Good(t *testing.T) { // --- Execute tests --- -func TestDispatch_Execute_Bad_UnknownAgent(t *testing.T) { +func TestDispatch_Execute_Bad_UnknownAgent_Good(t *testing.T) { srv := httptest.NewServer(withVersion(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) }))) @@ -144,7 +144,7 @@ func TestDispatch_Execute_Bad_UnknownAgent(t *testing.T) { assert.Contains(t, err.Error(), "unknown agent") } -func TestDispatch_Execute_Bad_InvalidQueueDir(t *testing.T) { +func TestDispatch_Execute_Bad_InvalidQueueDir_Good(t *testing.T) { spinner := newTestSpinner(map[string]agentci.AgentConfig{ "darbs-claude": { Host: "localhost", @@ -209,7 +209,7 @@ func TestDispatch_TicketJSON_Good(t *testing.T) { assert.False(t, hasToken, "forge_token must not be in ticket JSON") } -func TestDispatch_TicketJSON_Good_DualRun(t *testing.T) { +func TestDispatch_TicketJSON_Good_DualRun_Good(t *testing.T) { ticket := DispatchTicket{ ID: "test-dual", RepoOwner: "host-uk", @@ -231,7 +231,7 @@ func TestDispatch_TicketJSON_Good_DualRun(t *testing.T) { assert.Equal(t, "gemini-1.5-pro", roundtrip.VerifyModel) } -func TestDispatch_TicketJSON_Good_OmitsEmptyModelRunner(t *testing.T) { +func TestDispatch_TicketJSON_Good_OmitsEmptyModelRunner_Good(t *testing.T) { ticket := DispatchTicket{ ID: "test-1", RepoOwner: "host-uk", @@ -254,7 +254,7 @@ func TestDispatch_TicketJSON_Good_OmitsEmptyModelRunner(t *testing.T) { assert.False(t, hasRunner, "runner should be omitted when empty") } -func TestDispatch_runRemote_Good_EscapesPath(t *testing.T) { +func TestDispatch_runRemote_Good_EscapesPath_Good(t *testing.T) { outputPath := filepath.Join(t.TempDir(), "ssh-output.txt") toolPath := writeFakeSSHCommand(t, outputPath) t.Setenv("PATH", toolPath+":"+os.Getenv("PATH")) @@ -275,7 +275,7 @@ func TestDispatch_runRemote_Good_EscapesPath(t *testing.T) { assert.Contains(t, string(output), "rm '-f' '"+dangerousPath+"'\n") } -func TestDispatch_secureTransfer_Good_EscapesPath(t *testing.T) { +func TestDispatch_secureTransfer_Good_EscapesPath_Good(t *testing.T) { outputPath := filepath.Join(t.TempDir(), "ssh-output.txt") toolPath := writeFakeSSHCommand(t, outputPath) t.Setenv("PATH", toolPath+":"+os.Getenv("PATH")) @@ -301,7 +301,7 @@ func TestDispatch_secureTransfer_Good_EscapesPath(t *testing.T) { assert.Equal(t, "hello", string(input)) } -func TestDispatch_TicketJSON_Good_ModelRunnerVariants(t *testing.T) { +func TestDispatch_TicketJSON_Good_ModelRunnerVariants_Good(t *testing.T) { tests := []struct { name string model string @@ -338,7 +338,7 @@ func TestDispatch_TicketJSON_Good_ModelRunnerVariants(t *testing.T) { } } -func TestDispatch_Execute_Good_PostsComment(t *testing.T) { +func TestDispatch_Execute_Good_PostsComment_Good(t *testing.T) { var commentPosted bool var commentBody string diff --git a/jobrunner/handlers/enable_auto_merge.go b/jobrunner/handlers/enable_auto_merge.go index 5e53250..4aaf987 100644 --- a/jobrunner/handlers/enable_auto_merge.go +++ b/jobrunner/handlers/enable_auto_merge.go @@ -22,12 +22,14 @@ func NewEnableAutoMergeHandler(f *forge.Client) *EnableAutoMergeHandler { } // Name returns the handler identifier. +// Usage: Name(...) func (h *EnableAutoMergeHandler) Name() string { return "enable_auto_merge" } // Match returns true when the PR is open, not a draft, mergeable, checks // are passing, and there are no unresolved review threads. +// Usage: Match(...) func (h *EnableAutoMergeHandler) Match(signal *jobrunner.PipelineSignal) bool { return signal.PRState == "OPEN" && !signal.IsDraft && @@ -37,6 +39,7 @@ func (h *EnableAutoMergeHandler) Match(signal *jobrunner.PipelineSignal) bool { } // Execute merges the pull request with squash strategy. +// Usage: Execute(...) func (h *EnableAutoMergeHandler) Execute(ctx context.Context, signal *jobrunner.PipelineSignal) (*jobrunner.ActionResult, error) { start := time.Now() diff --git a/jobrunner/handlers/enable_auto_merge_test.go b/jobrunner/handlers/enable_auto_merge_test.go index ebb423c..e798ab8 100644 --- a/jobrunner/handlers/enable_auto_merge_test.go +++ b/jobrunner/handlers/enable_auto_merge_test.go @@ -28,7 +28,7 @@ func TestEnableAutoMerge_Match_Good(t *testing.T) { assert.True(t, h.Match(sig)) } -func TestEnableAutoMerge_Match_Bad_Draft(t *testing.T) { +func TestEnableAutoMerge_Match_Bad_Draft_Good(t *testing.T) { h := NewEnableAutoMergeHandler(nil) sig := &jobrunner.PipelineSignal{ PRState: "OPEN", @@ -41,7 +41,7 @@ func TestEnableAutoMerge_Match_Bad_Draft(t *testing.T) { assert.False(t, h.Match(sig)) } -func TestEnableAutoMerge_Match_Bad_UnresolvedThreads(t *testing.T) { +func TestEnableAutoMerge_Match_Bad_UnresolvedThreads_Good(t *testing.T) { h := NewEnableAutoMergeHandler(nil) sig := &jobrunner.PipelineSignal{ PRState: "OPEN", @@ -83,7 +83,7 @@ func TestEnableAutoMerge_Execute_Good(t *testing.T) { assert.Equal(t, "/api/v1/repos/host-uk/core-php/pulls/55/merge", capturedPath) } -func TestEnableAutoMerge_Execute_Bad_MergeFailed(t *testing.T) { +func TestEnableAutoMerge_Execute_Bad_MergeFailed_Good(t *testing.T) { srv := httptest.NewServer(withVersion(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusConflict) _ = json.NewEncoder(w).Encode(map[string]string{"message": "merge conflict"}) diff --git a/jobrunner/handlers/publish_draft.go b/jobrunner/handlers/publish_draft.go index 1f103fc..ffb9e5a 100644 --- a/jobrunner/handlers/publish_draft.go +++ b/jobrunner/handlers/publish_draft.go @@ -22,11 +22,13 @@ func NewPublishDraftHandler(f *forge.Client) *PublishDraftHandler { } // Name returns the handler identifier. +// Usage: Name(...) func (h *PublishDraftHandler) Name() string { return "publish_draft" } // Match returns true when the PR is a draft, open, and all checks have passed. +// Usage: Match(...) func (h *PublishDraftHandler) Match(signal *jobrunner.PipelineSignal) bool { return signal.IsDraft && signal.PRState == "OPEN" && @@ -34,6 +36,7 @@ func (h *PublishDraftHandler) Match(signal *jobrunner.PipelineSignal) bool { } // Execute marks the PR as no longer a draft. +// Usage: Execute(...) func (h *PublishDraftHandler) Execute(ctx context.Context, signal *jobrunner.PipelineSignal) (*jobrunner.ActionResult, error) { start := time.Now() diff --git a/jobrunner/handlers/publish_draft_test.go b/jobrunner/handlers/publish_draft_test.go index 8f37472..e3f6e9b 100644 --- a/jobrunner/handlers/publish_draft_test.go +++ b/jobrunner/handlers/publish_draft_test.go @@ -25,7 +25,7 @@ func TestPublishDraft_Match_Good(t *testing.T) { assert.True(t, h.Match(sig)) } -func TestPublishDraft_Match_Bad_NotDraft(t *testing.T) { +func TestPublishDraft_Match_Bad_NotDraft_Good(t *testing.T) { h := NewPublishDraftHandler(nil) sig := &jobrunner.PipelineSignal{ IsDraft: false, @@ -35,7 +35,7 @@ func TestPublishDraft_Match_Bad_NotDraft(t *testing.T) { assert.False(t, h.Match(sig)) } -func TestPublishDraft_Match_Bad_ChecksFailing(t *testing.T) { +func TestPublishDraft_Match_Bad_ChecksFailing_Good(t *testing.T) { h := NewPublishDraftHandler(nil) sig := &jobrunner.PipelineSignal{ IsDraft: true, diff --git a/jobrunner/handlers/resolve_threads.go b/jobrunner/handlers/resolve_threads.go index fc39318..f55e568 100644 --- a/jobrunner/handlers/resolve_threads.go +++ b/jobrunner/handlers/resolve_threads.go @@ -27,16 +27,19 @@ func NewDismissReviewsHandler(f *forge.Client) *DismissReviewsHandler { } // Name returns the handler identifier. +// Usage: Name(...) func (h *DismissReviewsHandler) Name() string { return "dismiss_reviews" } // Match returns true when the PR is open and has unresolved review threads. +// Usage: Match(...) func (h *DismissReviewsHandler) Match(signal *jobrunner.PipelineSignal) bool { return signal.PRState == "OPEN" && signal.HasUnresolvedThreads() } // Execute dismisses stale "request changes" reviews on the PR. +// Usage: Execute(...) func (h *DismissReviewsHandler) Execute(ctx context.Context, signal *jobrunner.PipelineSignal) (*jobrunner.ActionResult, error) { start := time.Now() diff --git a/jobrunner/handlers/resolve_threads_test.go b/jobrunner/handlers/resolve_threads_test.go index 72b18f7..239069b 100644 --- a/jobrunner/handlers/resolve_threads_test.go +++ b/jobrunner/handlers/resolve_threads_test.go @@ -25,7 +25,7 @@ func TestDismissReviews_Match_Good(t *testing.T) { assert.True(t, h.Match(sig)) } -func TestDismissReviews_Match_Bad_AllResolved(t *testing.T) { +func TestDismissReviews_Match_Bad_AllResolved_Good(t *testing.T) { h := NewDismissReviewsHandler(nil) sig := &jobrunner.PipelineSignal{ PRState: "OPEN", diff --git a/jobrunner/handlers/send_fix_command.go b/jobrunner/handlers/send_fix_command.go index c2afa68..c412cd5 100644 --- a/jobrunner/handlers/send_fix_command.go +++ b/jobrunner/handlers/send_fix_command.go @@ -23,12 +23,14 @@ func NewSendFixCommandHandler(f *forge.Client) *SendFixCommandHandler { } // Name returns the handler identifier. +// Usage: Name(...) func (h *SendFixCommandHandler) Name() string { return "send_fix_command" } // Match returns true when the PR is open and either has merge conflicts or // has unresolved threads with failing checks. +// Usage: Match(...) func (h *SendFixCommandHandler) Match(signal *jobrunner.PipelineSignal) bool { if signal.PRState != "OPEN" { return false @@ -43,6 +45,7 @@ func (h *SendFixCommandHandler) Match(signal *jobrunner.PipelineSignal) bool { } // Execute posts a comment on the PR asking for a fix. +// Usage: Execute(...) func (h *SendFixCommandHandler) Execute(ctx context.Context, signal *jobrunner.PipelineSignal) (*jobrunner.ActionResult, error) { start := time.Now() diff --git a/jobrunner/handlers/send_fix_command_test.go b/jobrunner/handlers/send_fix_command_test.go index dcd7e69..7b43cfd 100644 --- a/jobrunner/handlers/send_fix_command_test.go +++ b/jobrunner/handlers/send_fix_command_test.go @@ -15,7 +15,7 @@ import ( "dappco.re/go/core/scm/jobrunner" ) -func TestSendFixCommand_Match_Good_Conflicting(t *testing.T) { +func TestSendFixCommand_Match_Good_Conflicting_Good(t *testing.T) { h := NewSendFixCommandHandler(nil) sig := &jobrunner.PipelineSignal{ PRState: "OPEN", @@ -24,7 +24,7 @@ func TestSendFixCommand_Match_Good_Conflicting(t *testing.T) { assert.True(t, h.Match(sig)) } -func TestSendFixCommand_Match_Good_UnresolvedThreads(t *testing.T) { +func TestSendFixCommand_Match_Good_UnresolvedThreads_Good(t *testing.T) { h := NewSendFixCommandHandler(nil) sig := &jobrunner.PipelineSignal{ PRState: "OPEN", @@ -36,7 +36,7 @@ func TestSendFixCommand_Match_Good_UnresolvedThreads(t *testing.T) { assert.True(t, h.Match(sig)) } -func TestSendFixCommand_Match_Bad_Clean(t *testing.T) { +func TestSendFixCommand_Match_Bad_Clean_Good(t *testing.T) { h := NewSendFixCommandHandler(nil) sig := &jobrunner.PipelineSignal{ PRState: "OPEN", @@ -48,7 +48,7 @@ func TestSendFixCommand_Match_Bad_Clean(t *testing.T) { assert.False(t, h.Match(sig)) } -func TestSendFixCommand_Execute_Good_Conflict(t *testing.T) { +func TestSendFixCommand_Execute_Good_Conflict_Good(t *testing.T) { var capturedMethod string var capturedPath string var capturedBody string diff --git a/jobrunner/handlers/tick_parent.go b/jobrunner/handlers/tick_parent.go index e507067..ba1b0e5 100644 --- a/jobrunner/handlers/tick_parent.go +++ b/jobrunner/handlers/tick_parent.go @@ -27,17 +27,20 @@ func NewTickParentHandler(f *forge.Client) *TickParentHandler { } // Name returns the handler identifier. +// Usage: Name(...) func (h *TickParentHandler) Name() string { return "tick_parent" } // Match returns true when the child PR has been merged. +// Usage: Match(...) func (h *TickParentHandler) Match(signal *jobrunner.PipelineSignal) bool { return signal.PRState == "MERGED" } // Execute fetches the epic body, replaces the unchecked checkbox for the // child issue with a checked one, updates the epic, and closes the child issue. +// Usage: Execute(...) func (h *TickParentHandler) Execute(ctx context.Context, signal *jobrunner.PipelineSignal) (*jobrunner.ActionResult, error) { start := time.Now() diff --git a/jobrunner/handlers/tick_parent_test.go b/jobrunner/handlers/tick_parent_test.go index 9b95a08..17c37a9 100644 --- a/jobrunner/handlers/tick_parent_test.go +++ b/jobrunner/handlers/tick_parent_test.go @@ -25,7 +25,7 @@ func TestTickParent_Match_Good(t *testing.T) { assert.True(t, h.Match(sig)) } -func TestTickParent_Match_Bad_Open(t *testing.T) { +func TestTickParent_Match_Bad_Open_Good(t *testing.T) { h := NewTickParentHandler(nil) sig := &jobrunner.PipelineSignal{ PRState: "OPEN", diff --git a/jobrunner/journal_test.go b/jobrunner/journal_test.go index 9ba678b..ff928a7 100644 --- a/jobrunner/journal_test.go +++ b/jobrunner/journal_test.go @@ -115,7 +115,7 @@ func TestJournal_Append_Good(t *testing.T) { assert.Equal(t, 2, lines, "expected two JSONL lines after two appends") } -func TestJournal_Append_Bad_PathTraversal(t *testing.T) { +func TestJournal_Append_Bad_PathTraversal_Good(t *testing.T) { dir := t.TempDir() j, err := NewJournal(dir) @@ -197,7 +197,7 @@ func TestJournal_Append_Bad_PathTraversal(t *testing.T) { } } -func TestJournal_Append_Good_ValidNames(t *testing.T) { +func TestJournal_Append_Good_ValidNames_Good(t *testing.T) { dir := t.TempDir() j, err := NewJournal(dir) @@ -232,7 +232,7 @@ func TestJournal_Append_Good_ValidNames(t *testing.T) { } } -func TestJournal_Append_Bad_NilSignal(t *testing.T) { +func TestJournal_Append_Bad_NilSignal_Good(t *testing.T) { dir := t.TempDir() j, err := NewJournal(dir) @@ -248,7 +248,7 @@ func TestJournal_Append_Bad_NilSignal(t *testing.T) { assert.Contains(t, err.Error(), "signal is required") } -func TestJournal_Append_Bad_NilResult(t *testing.T) { +func TestJournal_Append_Bad_NilResult_Good(t *testing.T) { dir := t.TempDir() j, err := NewJournal(dir) diff --git a/jobrunner/poller_test.go b/jobrunner/poller_test.go index 5f26dc6..283c561 100644 --- a/jobrunner/poller_test.go +++ b/jobrunner/poller_test.go @@ -120,7 +120,7 @@ func TestPoller_RunOnce_Good(t *testing.T) { assert.Equal(t, 1, p.Cycle()) } -func TestPoller_RunOnce_Good_NoSignals(t *testing.T) { +func TestPoller_RunOnce_Good_NoSignals_Good(t *testing.T) { src := &mockSource{ name: "empty-source", signals: nil, @@ -151,7 +151,7 @@ func TestPoller_RunOnce_Good_NoSignals(t *testing.T) { assert.Equal(t, 1, p.Cycle()) } -func TestPoller_RunOnce_Good_NoMatchingHandler(t *testing.T) { +func TestPoller_RunOnce_Good_NoMatchingHandler_Good(t *testing.T) { sig := &PipelineSignal{ EpicNumber: 5, ChildNumber: 8, @@ -192,7 +192,7 @@ func TestPoller_RunOnce_Good_NoMatchingHandler(t *testing.T) { assert.Empty(t, src.reports) } -func TestPoller_RunOnce_Good_DryRun(t *testing.T) { +func TestPoller_RunOnce_Good_DryRun_Good(t *testing.T) { sig := &PipelineSignal{ EpicNumber: 1, ChildNumber: 3, diff --git a/jobrunner/types_test.go b/jobrunner/types_test.go index 0b01fb0..5578e8e 100644 --- a/jobrunner/types_test.go +++ b/jobrunner/types_test.go @@ -27,7 +27,7 @@ func TestPipelineSignal_HasUnresolvedThreads_Good(t *testing.T) { assert.True(t, sig.HasUnresolvedThreads()) } -func TestPipelineSignal_HasUnresolvedThreads_Bad_AllResolved(t *testing.T) { +func TestPipelineSignal_HasUnresolvedThreads_Bad_AllResolved_Good(t *testing.T) { sig := &PipelineSignal{ ThreadsTotal: 4, ThreadsResolved: 4, diff --git a/manifest/compile_test.go b/manifest/compile_test.go index 56a19b6..c537f26 100644 --- a/manifest/compile_test.go +++ b/manifest/compile_test.go @@ -37,7 +37,7 @@ func TestCompile_Good(t *testing.T) { assert.NotEmpty(t, cm.BuiltAt) } -func TestCompile_Good_WithSigning(t *testing.T) { +func TestCompile_Good_WithSigning_Good(t *testing.T) { pub, priv, err := ed25519.GenerateKey(rand.Reader) require.NoError(t, err) @@ -60,20 +60,20 @@ func TestCompile_Good_WithSigning(t *testing.T) { assert.True(t, ok) } -func TestCompile_Bad_NilManifest(t *testing.T) { +func TestCompile_Bad_NilManifest_Good(t *testing.T) { _, err := Compile(nil, CompileOptions{}) assert.Error(t, err) assert.Contains(t, err.Error(), "nil manifest") } -func TestCompile_Bad_MissingCode(t *testing.T) { +func TestCompile_Bad_MissingCode_Good(t *testing.T) { m := &Manifest{Version: "1.0.0"} _, err := Compile(m, CompileOptions{}) assert.Error(t, err) assert.Contains(t, err.Error(), "missing code") } -func TestCompile_Bad_MissingVersion(t *testing.T) { +func TestCompile_Bad_MissingVersion_Good(t *testing.T) { m := &Manifest{Code: "test"} _, err := Compile(m, CompileOptions{}) assert.Error(t, err) @@ -162,13 +162,13 @@ func TestLoadCompiled_Good(t *testing.T) { assert.Equal(t, "ddd444", cm.Commit) } -func TestLoadCompiled_Bad_NotFound(t *testing.T) { +func TestLoadCompiled_Bad_NotFound_Good(t *testing.T) { medium := io.NewMockMedium() _, err := LoadCompiled(medium, "/missing") assert.Error(t, err) } -func TestCompile_Good_MinimalOptions(t *testing.T) { +func TestCompile_Good_MinimalOptions_Good(t *testing.T) { m := &Manifest{ Code: "minimal", Name: "Minimal", diff --git a/manifest/loader_test.go b/manifest/loader_test.go index b128d64..6b49579 100644 --- a/manifest/loader_test.go +++ b/manifest/loader_test.go @@ -27,7 +27,7 @@ slots: assert.Equal(t, "main-content", m.Slots["C"]) } -func TestLoad_Bad_NoManifest(t *testing.T) { +func TestLoad_Bad_NoManifest_Good(t *testing.T) { fs := io.NewMockMedium() _, err := Load(fs, ".") assert.Error(t, err) @@ -50,7 +50,7 @@ func TestLoadVerified_Good(t *testing.T) { assert.Equal(t, "signed-app", loaded.Code) } -func TestLoadVerified_Bad_Tampered(t *testing.T) { +func TestLoadVerified_Bad_Tampered_Good(t *testing.T) { pub, priv, _ := ed25519.GenerateKey(nil) m := &Manifest{Code: "app", Version: "1.0.0"} _ = Sign(m, priv) diff --git a/manifest/manifest.go b/manifest/manifest.go index edb68f1..7bcf5c2 100644 --- a/manifest/manifest.go +++ b/manifest/manifest.go @@ -43,6 +43,7 @@ type ElementSpec struct { // IsProvider returns true if this manifest declares provider fields // (namespace and binary), indicating it is a runtime provider. +// Usage: IsProvider(...) func (m *Manifest) IsProvider() bool { return m.Namespace != "" && m.Binary != "" } diff --git a/manifest/manifest_test.go b/manifest/manifest_test.go index cc2e000..08b6d5c 100644 --- a/manifest/manifest_test.go +++ b/manifest/manifest_test.go @@ -66,7 +66,7 @@ func TestManifest_SlotNames_Good(t *testing.T) { assert.Len(t, names, 2) } -func TestParse_Good_WithDaemons(t *testing.T) { +func TestParse_Good_WithDaemons_Good(t *testing.T) { raw := ` code: my-service name: My Service @@ -126,7 +126,7 @@ func TestManifest_DefaultDaemon_Good(t *testing.T) { assert.True(t, spec.Default) } -func TestManifest_DefaultDaemon_Bad_NoDaemons(t *testing.T) { +func TestManifest_DefaultDaemon_Bad_NoDaemons_Good(t *testing.T) { m := Manifest{} name, spec, ok := m.DefaultDaemon() assert.False(t, ok) @@ -134,7 +134,7 @@ func TestManifest_DefaultDaemon_Bad_NoDaemons(t *testing.T) { assert.Empty(t, spec.Binary) } -func TestManifest_DefaultDaemon_Bad_MultipleDefaults(t *testing.T) { +func TestManifest_DefaultDaemon_Bad_MultipleDefaults_Good(t *testing.T) { m := Manifest{ Daemons: map[string]DaemonSpec{ "api": {Binary: "./bin/api", Default: true}, @@ -145,7 +145,7 @@ func TestManifest_DefaultDaemon_Bad_MultipleDefaults(t *testing.T) { assert.False(t, ok) } -func TestManifest_DefaultDaemon_Bad_MultipleNoneDefault(t *testing.T) { +func TestManifest_DefaultDaemon_Bad_MultipleNoneDefault_Good(t *testing.T) { m := Manifest{ Daemons: map[string]DaemonSpec{ "api": {Binary: "./bin/api"}, @@ -156,7 +156,7 @@ func TestManifest_DefaultDaemon_Bad_MultipleNoneDefault(t *testing.T) { assert.False(t, ok) } -func TestParse_Good_WithProviderFields(t *testing.T) { +func TestParse_Good_WithProviderFields_Good(t *testing.T) { raw := ` code: cool-widget name: Cool Widget Dashboard @@ -204,22 +204,22 @@ func TestManifest_IsProvider_Good(t *testing.T) { assert.True(t, m.IsProvider()) } -func TestManifest_IsProvider_Bad_NoNamespace(t *testing.T) { +func TestManifest_IsProvider_Bad_NoNamespace_Good(t *testing.T) { m := Manifest{Binary: "./test"} assert.False(t, m.IsProvider()) } -func TestManifest_IsProvider_Bad_NoBinary(t *testing.T) { +func TestManifest_IsProvider_Bad_NoBinary_Good(t *testing.T) { m := Manifest{Namespace: "/api/v1/test"} assert.False(t, m.IsProvider()) } -func TestManifest_IsProvider_Bad_Empty(t *testing.T) { +func TestManifest_IsProvider_Bad_Empty_Good(t *testing.T) { m := Manifest{} assert.False(t, m.IsProvider()) } -func TestManifest_DefaultDaemon_Good_SingleImplicit(t *testing.T) { +func TestManifest_DefaultDaemon_Good_SingleImplicit_Good(t *testing.T) { m := Manifest{ Daemons: map[string]DaemonSpec{ "server": { diff --git a/manifest/sign_test.go b/manifest/sign_test.go index 41e2d54..5b57c96 100644 --- a/manifest/sign_test.go +++ b/manifest/sign_test.go @@ -31,7 +31,7 @@ func TestSignAndVerify_Good(t *testing.T) { assert.True(t, ok) } -func TestVerify_Bad_Tampered(t *testing.T) { +func TestVerify_Bad_Tampered_Good(t *testing.T) { pub, priv, _ := ed25519.GenerateKey(nil) m := &Manifest{Code: "test-app", Version: "1.0.0"} _ = Sign(m, priv) @@ -43,7 +43,7 @@ func TestVerify_Bad_Tampered(t *testing.T) { assert.False(t, ok) } -func TestVerify_Bad_Unsigned(t *testing.T) { +func TestVerify_Bad_Unsigned_Good(t *testing.T) { pub, _, _ := ed25519.GenerateKey(nil) m := &Manifest{Code: "test-app"} diff --git a/marketplace/builder_test.go b/marketplace/builder_test.go index f0ea836..00a7df3 100644 --- a/marketplace/builder_test.go +++ b/marketplace/builder_test.go @@ -38,7 +38,7 @@ func writeCoreJSON(t *testing.T, dir, code, name, version string) { require.NoError(t, os.WriteFile(filepath.Join(dir, "core.json"), data, 0644)) } -func TestBuildFromDirs_Good_ManifestYAML(t *testing.T) { +func TestBuildFromDirs_Good_ManifestYAML_Good(t *testing.T) { root := t.TempDir() modDir := filepath.Join(root, "my-widget") require.NoError(t, os.MkdirAll(modDir, 0755)) @@ -55,7 +55,7 @@ func TestBuildFromDirs_Good_ManifestYAML(t *testing.T) { assert.Equal(t, IndexVersion, idx.Version) } -func TestBuildFromDirs_Good_CoreJSON(t *testing.T) { +func TestBuildFromDirs_Good_CoreJSON_Good(t *testing.T) { root := t.TempDir() modDir := filepath.Join(root, "compiled-mod") require.NoError(t, os.MkdirAll(modDir, 0755)) @@ -70,7 +70,7 @@ func TestBuildFromDirs_Good_CoreJSON(t *testing.T) { assert.Equal(t, "Compiled Module", idx.Modules[0].Name) } -func TestBuildFromDirs_Good_PrefersCompiledOverSource(t *testing.T) { +func TestBuildFromDirs_Good_PrefersCompiledOverSource_Good(t *testing.T) { root := t.TempDir() modDir := filepath.Join(root, "dual-mod") require.NoError(t, os.MkdirAll(modDir, 0755)) @@ -86,7 +86,7 @@ func TestBuildFromDirs_Good_PrefersCompiledOverSource(t *testing.T) { assert.Equal(t, "compiled-code", idx.Modules[0].Code) } -func TestBuildFromDirs_Good_SkipsNoManifest(t *testing.T) { +func TestBuildFromDirs_Good_SkipsNoManifest_Good(t *testing.T) { root := t.TempDir() // Directory with no manifest. require.NoError(t, os.MkdirAll(filepath.Join(root, "no-manifest"), 0755)) @@ -101,7 +101,7 @@ func TestBuildFromDirs_Good_SkipsNoManifest(t *testing.T) { assert.Len(t, idx.Modules, 1) } -func TestBuildFromDirs_Good_Deduplicates(t *testing.T) { +func TestBuildFromDirs_Good_Deduplicates_Good(t *testing.T) { dir1 := t.TempDir() dir2 := t.TempDir() @@ -120,7 +120,7 @@ func TestBuildFromDirs_Good_Deduplicates(t *testing.T) { assert.Equal(t, "shared", idx.Modules[0].Code) } -func TestBuildFromDirs_Good_SortsByCode(t *testing.T) { +func TestBuildFromDirs_Good_SortsByCode_Good(t *testing.T) { root := t.TempDir() for _, name := range []string{"charlie", "alpha", "bravo"} { d := filepath.Join(root, name) @@ -137,7 +137,7 @@ func TestBuildFromDirs_Good_SortsByCode(t *testing.T) { assert.Equal(t, "charlie", idx.Modules[2].Code) } -func TestBuildFromDirs_Good_EmptyDir(t *testing.T) { +func TestBuildFromDirs_Good_EmptyDir_Good(t *testing.T) { root := t.TempDir() b := &Builder{} idx, err := b.BuildFromDirs(root) @@ -146,14 +146,14 @@ func TestBuildFromDirs_Good_EmptyDir(t *testing.T) { assert.Equal(t, IndexVersion, idx.Version) } -func TestBuildFromDirs_Good_NonexistentDir(t *testing.T) { +func TestBuildFromDirs_Good_NonexistentDir_Good(t *testing.T) { b := &Builder{} idx, err := b.BuildFromDirs("/nonexistent/path") require.NoError(t, err) assert.Empty(t, idx.Modules) } -func TestBuildFromDirs_Good_NoRepoURLWithoutConfig(t *testing.T) { +func TestBuildFromDirs_Good_NoRepoURLWithoutConfig_Good(t *testing.T) { root := t.TempDir() modDir := filepath.Join(root, "mod") require.NoError(t, os.MkdirAll(modDir, 0755)) @@ -177,7 +177,7 @@ func TestBuildFromManifests_Good(t *testing.T) { assert.Equal(t, IndexVersion, idx.Version) } -func TestBuildFromManifests_Good_SkipsNil(t *testing.T) { +func TestBuildFromManifests_Good_SkipsNil_Good(t *testing.T) { manifests := []*manifest.Manifest{ nil, {Code: "valid", Name: "Valid"}, @@ -188,7 +188,7 @@ func TestBuildFromManifests_Good_SkipsNil(t *testing.T) { assert.Equal(t, "valid", idx.Modules[0].Code) } -func TestBuildFromManifests_Good_Deduplicates(t *testing.T) { +func TestBuildFromManifests_Good_Deduplicates_Good(t *testing.T) { manifests := []*manifest.Manifest{ {Code: "dup", Name: "First"}, {Code: "dup", Name: "Second"}, @@ -220,7 +220,7 @@ func TestWriteIndex_Good(t *testing.T) { assert.Equal(t, "test-mod", parsed.Modules[0].Code) } -func TestWriteIndex_Good_RoundTrip(t *testing.T) { +func TestWriteIndex_Good_RoundTrip_Good(t *testing.T) { dir := t.TempDir() path := filepath.Join(dir, "index.json") diff --git a/marketplace/discovery_test.go b/marketplace/discovery_test.go index c306533..f1f484c 100644 --- a/marketplace/discovery_test.go +++ b/marketplace/discovery_test.go @@ -58,7 +58,7 @@ binary: ./data-viz assert.True(t, codes["data-viz"]) } -func TestDiscoverProviders_Good_SkipNonProvider(t *testing.T) { +func TestDiscoverProviders_Good_SkipNonProvider_Good(t *testing.T) { dir := t.TempDir() // This has a valid manifest but no namespace/binary — not a provider. @@ -83,7 +83,7 @@ binary: ./real-provider assert.Equal(t, "real-provider", providers[0].Manifest.Code) } -func TestDiscoverProviders_Good_SkipNoManifest(t *testing.T) { +func TestDiscoverProviders_Good_SkipNoManifest_Good(t *testing.T) { dir := t.TempDir() // Directory with no manifest. @@ -104,7 +104,7 @@ binary: ./good-provider assert.Equal(t, "good-provider", providers[0].Manifest.Code) } -func TestDiscoverProviders_Good_SkipInvalidManifest(t *testing.T) { +func TestDiscoverProviders_Good_SkipInvalidManifest_Good(t *testing.T) { dir := t.TempDir() // Directory with invalid YAML. @@ -121,7 +121,7 @@ func TestDiscoverProviders_Good_SkipInvalidManifest(t *testing.T) { assert.Empty(t, providers) } -func TestDiscoverProviders_Good_EmptyDir(t *testing.T) { +func TestDiscoverProviders_Good_EmptyDir_Good(t *testing.T) { dir := t.TempDir() providers, err := DiscoverProviders(dir) @@ -129,13 +129,13 @@ func TestDiscoverProviders_Good_EmptyDir(t *testing.T) { assert.Empty(t, providers) } -func TestDiscoverProviders_Good_NonexistentDir(t *testing.T) { +func TestDiscoverProviders_Good_NonexistentDir_Good(t *testing.T) { providers, err := DiscoverProviders("/tmp/nonexistent-discovery-test-dir") require.NoError(t, err) assert.Nil(t, providers) } -func TestDiscoverProviders_Good_SkipFiles(t *testing.T) { +func TestDiscoverProviders_Good_SkipFiles_Good(t *testing.T) { dir := t.TempDir() // Create a regular file (not a directory). @@ -146,7 +146,7 @@ func TestDiscoverProviders_Good_SkipFiles(t *testing.T) { assert.Empty(t, providers) } -func TestDiscoverProviders_Good_ProviderDir(t *testing.T) { +func TestDiscoverProviders_Good_ProviderDir_Good(t *testing.T) { dir := t.TempDir() createProviderDir(t, dir, "test-prov", ` @@ -194,7 +194,7 @@ func TestProviderRegistry_LoadSave_Good(t *testing.T) { assert.True(t, entry.AutoStart) } -func TestProviderRegistry_Load_Good_NonexistentFile(t *testing.T) { +func TestProviderRegistry_Load_Good_NonexistentFile_Good(t *testing.T) { reg, err := LoadProviderRegistry("/tmp/nonexistent-registry-test.yaml") require.NoError(t, err) assert.Equal(t, 1, reg.Version) @@ -233,7 +233,7 @@ func TestProviderRegistry_Remove_Good(t *testing.T) { assert.False(t, ok) } -func TestProviderRegistry_Get_Bad_NotFound(t *testing.T) { +func TestProviderRegistry_Get_Bad_NotFound_Good(t *testing.T) { reg := &ProviderRegistryFile{ Version: 1, Providers: map[string]ProviderRegistryEntry{}, diff --git a/marketplace/installer_test.go b/marketplace/installer_test.go index f7583a3..ec94bce 100644 --- a/marketplace/installer_test.go +++ b/marketplace/installer_test.go @@ -103,7 +103,7 @@ func TestInstall_Good(t *testing.T) { assert.Contains(t, raw, `"version":"1.0"`) } -func TestInstall_Good_Signed(t *testing.T) { +func TestInstall_Good_Signed_Good(t *testing.T) { repo, signKey := createSignedTestRepo(t, "signed-mod", "2.0") modulesDir := filepath.Join(t.TempDir(), "modules") @@ -124,7 +124,7 @@ func TestInstall_Good_Signed(t *testing.T) { assert.Contains(t, raw, `"version":"2.0"`) } -func TestInstall_Bad_AlreadyInstalled(t *testing.T) { +func TestInstall_Bad_AlreadyInstalled_Good(t *testing.T) { repo := createTestRepo(t, "dup-mod", "1.0") modulesDir := filepath.Join(t.TempDir(), "modules") @@ -141,7 +141,7 @@ func TestInstall_Bad_AlreadyInstalled(t *testing.T) { assert.Contains(t, err.Error(), "already installed") } -func TestInstall_Bad_InvalidSignature(t *testing.T) { +func TestInstall_Bad_InvalidSignature_Good(t *testing.T) { // Sign with key A, verify with key B repo, _ := createSignedTestRepo(t, "bad-sig", "1.0") _, wrongKey := createSignedTestRepo(t, "dummy", "1.0") // different key @@ -165,7 +165,7 @@ func TestInstall_Bad_InvalidSignature(t *testing.T) { assert.True(t, os.IsNotExist(statErr), "directory should be cleaned up on failure") } -func TestInstall_Bad_PathTraversalCode(t *testing.T) { +func TestInstall_Bad_PathTraversalCode_Good(t *testing.T) { repo := createTestRepo(t, "safe-mod", "1.0") modulesDir := filepath.Join(t.TempDir(), "modules") @@ -211,7 +211,7 @@ func TestRemove_Good(t *testing.T) { assert.Error(t, err) } -func TestRemove_Bad_NotInstalled(t *testing.T) { +func TestRemove_Bad_NotInstalled_Good(t *testing.T) { st, err := store.New(":memory:") require.NoError(t, err) defer st.Close() @@ -222,7 +222,7 @@ func TestRemove_Bad_NotInstalled(t *testing.T) { assert.Contains(t, err.Error(), "not installed") } -func TestRemove_Bad_PathTraversalCode(t *testing.T) { +func TestRemove_Bad_PathTraversalCode_Good(t *testing.T) { baseDir := t.TempDir() modulesDir := filepath.Join(baseDir, "modules") escapeDir := filepath.Join(baseDir, "escape") @@ -269,7 +269,7 @@ func TestInstalled_Good(t *testing.T) { assert.True(t, codes["mod-b"]) } -func TestInstalled_Good_Empty(t *testing.T) { +func TestInstalled_Good_Empty_Good(t *testing.T) { st, err := store.New(":memory:") require.NoError(t, err) defer st.Close() @@ -308,7 +308,7 @@ func TestUpdate_Good(t *testing.T) { assert.Equal(t, "Updated Module", installed[0].Name) } -func TestUpdate_Bad_PathTraversalCode(t *testing.T) { +func TestUpdate_Bad_PathTraversalCode_Good(t *testing.T) { modulesDir := filepath.Join(t.TempDir(), "modules") st, err := store.New(":memory:") diff --git a/marketplace/marketplace_test.go b/marketplace/marketplace_test.go index 7bab66a..394d82b 100644 --- a/marketplace/marketplace_test.go +++ b/marketplace/marketplace_test.go @@ -60,7 +60,7 @@ func TestFind_Good(t *testing.T) { assert.Equal(t, "XMRig", m.Name) } -func TestFind_Bad_NotFound(t *testing.T) { +func TestFind_Bad_NotFound_Good(t *testing.T) { idx := &Index{} _, ok := idx.Find("nope") assert.False(t, ok) diff --git a/pkg/api/provider_test.go b/pkg/api/provider_test.go index cbd7f68..6c89554 100644 --- a/pkg/api/provider_test.go +++ b/pkg/api/provider_test.go @@ -164,7 +164,7 @@ func TestScmProvider_GetMarketplaceItem_Bad(t *testing.T) { assert.Equal(t, http.StatusNotFound, w.Code) } -func TestScmProvider_GetMarketplaceItem_Bad_PathTraversal(t *testing.T) { +func TestScmProvider_GetMarketplaceItem_Bad_PathTraversal_Good(t *testing.T) { idx := &marketplace.Index{Version: 1} p := scmapi.NewProvider(idx, nil, nil, nil) diff --git a/plugin/installer.go b/plugin/installer.go index 961d480..74f3cfb 100644 --- a/plugin/installer.go +++ b/plugin/installer.go @@ -32,6 +32,7 @@ func NewInstaller(m io.Medium, registry *Registry) *Installer { // Install downloads and installs a plugin from GitHub. // The source format is "org/repo" or "org/repo@version". +// Usage: Install(...) func (i *Installer) Install(ctx context.Context, source string) error { org, repo, version, err := ParseSource(source) if err != nil { @@ -96,6 +97,7 @@ func (i *Installer) Install(ctx context.Context, source string) error { } // Update updates a plugin to the latest version. +// Usage: Update(...) func (i *Installer) Update(ctx context.Context, name string) error { safeName, pluginDir, err := i.resolvePluginPath(name) if err != nil { @@ -130,6 +132,7 @@ func (i *Installer) Update(ctx context.Context, name string) error { } // Remove uninstalls a plugin by removing its files and registry entry. +// Usage: Remove(...) func (i *Installer) Remove(name string) error { safeName, pluginDir, err := i.resolvePluginPath(name) if err != nil { diff --git a/plugin/installer_test.go b/plugin/installer_test.go index 2b03eb5..19c68b4 100644 --- a/plugin/installer_test.go +++ b/plugin/installer_test.go @@ -25,7 +25,7 @@ func TestNewInstaller_Good(t *testing.T) { // ── Install error paths ──────────────────────────────────────────── -func TestInstall_Bad_InvalidSource(t *testing.T) { +func TestInstall_Bad_InvalidSource_Good(t *testing.T) { m := io.NewMockMedium() reg := NewRegistry(m, "/plugins") inst := NewInstaller(m, reg) @@ -35,7 +35,7 @@ func TestInstall_Bad_InvalidSource(t *testing.T) { assert.Contains(t, err.Error(), "invalid source") } -func TestInstall_Bad_AlreadyInstalled(t *testing.T) { +func TestInstall_Bad_AlreadyInstalled_Good(t *testing.T) { m := io.NewMockMedium() reg := NewRegistry(m, "/plugins") _ = reg.Add(&PluginConfig{Name: "my-plugin", Version: "1.0.0"}) @@ -69,7 +69,7 @@ func TestRemove_Good(t *testing.T) { assert.False(t, m.Exists("/plugins/removable")) } -func TestRemove_Good_DirAlreadyGone(t *testing.T) { +func TestRemove_Good_DirAlreadyGone_Good(t *testing.T) { m := io.NewMockMedium() reg := NewRegistry(m, "/plugins") _ = reg.Add(&PluginConfig{Name: "ghost", Version: "1.0.0"}) @@ -83,7 +83,7 @@ func TestRemove_Good_DirAlreadyGone(t *testing.T) { assert.False(t, ok) } -func TestRemove_Bad_NotFound(t *testing.T) { +func TestRemove_Bad_NotFound_Good(t *testing.T) { m := io.NewMockMedium() reg := NewRegistry(m, "/plugins") inst := NewInstaller(m, reg) @@ -95,7 +95,7 @@ func TestRemove_Bad_NotFound(t *testing.T) { // ── Update error paths ───────────────────────────────────────────── -func TestUpdate_Bad_NotFound(t *testing.T) { +func TestUpdate_Bad_NotFound_Good(t *testing.T) { m := io.NewMockMedium() reg := NewRegistry(m, "/plugins") inst := NewInstaller(m, reg) @@ -107,7 +107,7 @@ func TestUpdate_Bad_NotFound(t *testing.T) { // ── ParseSource ──────────────────────────────────────────────────── -func TestParseSource_Good_OrgRepo(t *testing.T) { +func TestParseSource_Good_OrgRepo_Good(t *testing.T) { org, repo, version, err := ParseSource("host-uk/core-plugin") assert.NoError(t, err) assert.Equal(t, "host-uk", org) @@ -115,7 +115,7 @@ func TestParseSource_Good_OrgRepo(t *testing.T) { assert.Equal(t, "", version) } -func TestParseSource_Good_OrgRepoVersion(t *testing.T) { +func TestParseSource_Good_OrgRepoVersion_Good(t *testing.T) { org, repo, version, err := ParseSource("host-uk/core-plugin@v1.0.0") assert.NoError(t, err) assert.Equal(t, "host-uk", org) @@ -123,7 +123,7 @@ func TestParseSource_Good_OrgRepoVersion(t *testing.T) { assert.Equal(t, "v1.0.0", version) } -func TestParseSource_Good_VersionWithoutPrefix(t *testing.T) { +func TestParseSource_Good_VersionWithoutPrefix_Good(t *testing.T) { org, repo, version, err := ParseSource("org/repo@1.2.3") assert.NoError(t, err) assert.Equal(t, "org", org) @@ -131,37 +131,37 @@ func TestParseSource_Good_VersionWithoutPrefix(t *testing.T) { assert.Equal(t, "1.2.3", version) } -func TestParseSource_Bad_Empty(t *testing.T) { +func TestParseSource_Bad_Empty_Good(t *testing.T) { _, _, _, err := ParseSource("") assert.Error(t, err) assert.Contains(t, err.Error(), "source is empty") } -func TestParseSource_Bad_NoSlash(t *testing.T) { +func TestParseSource_Bad_NoSlash_Good(t *testing.T) { _, _, _, err := ParseSource("just-a-name") assert.Error(t, err) assert.Contains(t, err.Error(), "org/repo") } -func TestParseSource_Bad_TooManySlashes(t *testing.T) { +func TestParseSource_Bad_TooManySlashes_Good(t *testing.T) { _, _, _, err := ParseSource("a/b/c") assert.Error(t, err) assert.Contains(t, err.Error(), "org/repo") } -func TestParseSource_Bad_EmptyOrg(t *testing.T) { +func TestParseSource_Bad_EmptyOrg_Good(t *testing.T) { _, _, _, err := ParseSource("/repo") assert.Error(t, err) assert.Contains(t, err.Error(), "org/repo") } -func TestParseSource_Bad_EmptyRepo(t *testing.T) { +func TestParseSource_Bad_EmptyRepo_Good(t *testing.T) { _, _, _, err := ParseSource("org/") assert.Error(t, err) assert.Contains(t, err.Error(), "org/repo") } -func TestParseSource_Bad_EmptyVersion(t *testing.T) { +func TestParseSource_Bad_EmptyVersion_Good(t *testing.T) { _, _, _, err := ParseSource("org/repo@") assert.Error(t, err) assert.Contains(t, err.Error(), "version is empty") diff --git a/plugin/loader.go b/plugin/loader.go index 3e4aa41..d9149eb 100644 --- a/plugin/loader.go +++ b/plugin/loader.go @@ -25,6 +25,7 @@ func NewLoader(m io.Medium, baseDir string) *Loader { // Discover finds all plugin directories under baseDir and returns their manifests. // Directories without a valid plugin.json are silently skipped. +// Usage: Discover(...) func (l *Loader) Discover() ([]*Manifest, error) { entries, err := l.medium.List(l.baseDir) if err != nil { @@ -50,6 +51,7 @@ func (l *Loader) Discover() ([]*Manifest, error) { } // LoadPlugin loads a single plugin's manifest by name. +// Usage: LoadPlugin(...) func (l *Loader) LoadPlugin(name string) (*Manifest, error) { manifestPath := filepath.Join(l.baseDir, name, "plugin.json") manifest, err := LoadManifest(l.medium, manifestPath) diff --git a/plugin/loader_test.go b/plugin/loader_test.go index e2c2d16..b0f0753 100644 --- a/plugin/loader_test.go +++ b/plugin/loader_test.go @@ -45,7 +45,7 @@ func TestLoader_Discover_Good(t *testing.T) { assert.True(t, names["plugin-b"]) } -func TestLoader_Discover_Good_SkipsInvalidPlugins(t *testing.T) { +func TestLoader_Discover_Good_SkipsInvalidPlugins_Good(t *testing.T) { m := io.NewMockMedium() baseDir := "/home/user/.core/plugins" @@ -70,7 +70,7 @@ func TestLoader_Discover_Good_SkipsInvalidPlugins(t *testing.T) { assert.Equal(t, "good-plugin", manifests[0].Name) } -func TestLoader_Discover_Good_SkipsFiles(t *testing.T) { +func TestLoader_Discover_Good_SkipsFiles_Good(t *testing.T) { m := io.NewMockMedium() baseDir := "/home/user/.core/plugins" @@ -91,7 +91,7 @@ func TestLoader_Discover_Good_SkipsFiles(t *testing.T) { assert.Equal(t, "real-plugin", manifests[0].Name) } -func TestLoader_Discover_Good_EmptyDirectory(t *testing.T) { +func TestLoader_Discover_Good_EmptyDirectory_Good(t *testing.T) { m := io.NewMockMedium() baseDir := "/home/user/.core/plugins" m.Dirs[baseDir] = true @@ -122,7 +122,7 @@ func TestLoader_LoadPlugin_Good(t *testing.T) { assert.Equal(t, "1.0.0", manifest.Version) } -func TestLoader_LoadPlugin_Bad_NotFound(t *testing.T) { +func TestLoader_LoadPlugin_Bad_NotFound_Good(t *testing.T) { m := io.NewMockMedium() loader := NewLoader(m, "/home/user/.core/plugins") @@ -131,7 +131,7 @@ func TestLoader_LoadPlugin_Bad_NotFound(t *testing.T) { assert.Contains(t, err.Error(), "failed to load plugin") } -func TestLoader_LoadPlugin_Bad_InvalidManifest(t *testing.T) { +func TestLoader_LoadPlugin_Bad_InvalidManifest_Good(t *testing.T) { m := io.NewMockMedium() baseDir := "/home/user/.core/plugins" diff --git a/plugin/manifest.go b/plugin/manifest.go index 12e6bd9..dee658d 100644 --- a/plugin/manifest.go +++ b/plugin/manifest.go @@ -38,6 +38,7 @@ func LoadManifest(m io.Medium, path string) (*Manifest, error) { // Validate checks the manifest for required fields. // Returns an error if name, version, or entrypoint are missing. +// Usage: Validate(...) func (m *Manifest) Validate() error { if m.Name == "" { return coreerr.E("plugin.Manifest.Validate", "name is required", nil) diff --git a/plugin/manifest_test.go b/plugin/manifest_test.go index 690f88b..b9189d5 100644 --- a/plugin/manifest_test.go +++ b/plugin/manifest_test.go @@ -32,7 +32,7 @@ func TestLoadManifest_Good(t *testing.T) { assert.Equal(t, "0.5.0", manifest.MinVersion) } -func TestLoadManifest_Good_MinimalFields(t *testing.T) { +func TestLoadManifest_Good_MinimalFields_Good(t *testing.T) { m := io.NewMockMedium() m.Files["plugin.json"] = `{ "name": "minimal", @@ -49,7 +49,7 @@ func TestLoadManifest_Good_MinimalFields(t *testing.T) { assert.Empty(t, manifest.MinVersion) } -func TestLoadManifest_Bad_FileNotFound(t *testing.T) { +func TestLoadManifest_Bad_FileNotFound_Good(t *testing.T) { m := io.NewMockMedium() _, err := LoadManifest(m, "nonexistent/plugin.json") @@ -57,7 +57,7 @@ func TestLoadManifest_Bad_FileNotFound(t *testing.T) { assert.Contains(t, err.Error(), "failed to read manifest") } -func TestLoadManifest_Bad_InvalidJSON(t *testing.T) { +func TestLoadManifest_Bad_InvalidJSON_Good(t *testing.T) { m := io.NewMockMedium() m.Files["plugin.json"] = `{invalid json}` @@ -77,7 +77,7 @@ func TestManifest_Validate_Good(t *testing.T) { assert.NoError(t, err) } -func TestManifest_Validate_Bad_MissingName(t *testing.T) { +func TestManifest_Validate_Bad_MissingName_Good(t *testing.T) { manifest := &Manifest{ Version: "1.0.0", Entrypoint: "main.go", @@ -88,7 +88,7 @@ func TestManifest_Validate_Bad_MissingName(t *testing.T) { assert.Contains(t, err.Error(), "name is required") } -func TestManifest_Validate_Bad_MissingVersion(t *testing.T) { +func TestManifest_Validate_Bad_MissingVersion_Good(t *testing.T) { manifest := &Manifest{ Name: "test-plugin", Entrypoint: "main.go", @@ -99,7 +99,7 @@ func TestManifest_Validate_Bad_MissingVersion(t *testing.T) { assert.Contains(t, err.Error(), "version is required") } -func TestManifest_Validate_Bad_MissingEntrypoint(t *testing.T) { +func TestManifest_Validate_Bad_MissingEntrypoint_Good(t *testing.T) { manifest := &Manifest{ Name: "test-plugin", Version: "1.0.0", diff --git a/plugin/plugin.go b/plugin/plugin.go index 4e549a1..8256442 100644 --- a/plugin/plugin.go +++ b/plugin/plugin.go @@ -41,16 +41,21 @@ type BasePlugin struct { } // Name returns the plugin name. +// Usage: Name(...) func (p *BasePlugin) Name() string { return p.PluginName } // Version returns the plugin version. +// Usage: Version(...) func (p *BasePlugin) Version() string { return p.PluginVersion } // Init is a no-op default implementation. +// Usage: Init(...) func (p *BasePlugin) Init(_ context.Context) error { return nil } // Start is a no-op default implementation. +// Usage: Start(...) func (p *BasePlugin) Start(_ context.Context) error { return nil } // Stop is a no-op default implementation. +// Usage: Stop(...) func (p *BasePlugin) Stop(_ context.Context) error { return nil } diff --git a/plugin/plugin_test.go b/plugin/plugin_test.go index b5e66bc..e5cfde6 100644 --- a/plugin/plugin_test.go +++ b/plugin/plugin_test.go @@ -24,7 +24,7 @@ func TestBasePlugin_Good(t *testing.T) { assert.NoError(t, p.Stop(ctx)) } -func TestBasePlugin_Good_EmptyFields(t *testing.T) { +func TestBasePlugin_Good_EmptyFields_Good(t *testing.T) { p := &BasePlugin{} assert.Equal(t, "", p.Name()) @@ -36,6 +36,6 @@ func TestBasePlugin_Good_EmptyFields(t *testing.T) { assert.NoError(t, p.Stop(ctx)) } -func TestBasePlugin_Good_ImplementsPlugin(t *testing.T) { +func TestBasePlugin_Good_ImplementsPlugin_Good(t *testing.T) { var _ Plugin = &BasePlugin{} } diff --git a/plugin/registry.go b/plugin/registry.go index 9b0bbe5..5dcab50 100644 --- a/plugin/registry.go +++ b/plugin/registry.go @@ -32,6 +32,7 @@ func NewRegistry(m io.Medium, basePath string) *Registry { } // List returns all installed plugins sorted by name. +// Usage: List(...) func (r *Registry) List() []*PluginConfig { result := make([]*PluginConfig, 0, len(r.plugins)) for _, cfg := range r.plugins { @@ -45,12 +46,14 @@ func (r *Registry) List() []*PluginConfig { // Get returns a plugin by name. // The second return value indicates whether the plugin was found. +// Usage: Get(...) func (r *Registry) Get(name string) (*PluginConfig, bool) { cfg, ok := r.plugins[name] return cfg, ok } // Add registers a plugin in the registry. +// Usage: Add(...) func (r *Registry) Add(cfg *PluginConfig) error { if cfg.Name == "" { return coreerr.E("plugin.Registry.Add", "plugin name is required", nil) @@ -60,6 +63,7 @@ func (r *Registry) Add(cfg *PluginConfig) error { } // Remove unregisters a plugin from the registry. +// Usage: Remove(...) func (r *Registry) Remove(name string) error { if _, ok := r.plugins[name]; !ok { return coreerr.E("plugin.Registry.Remove", "plugin not found: "+name, nil) @@ -75,6 +79,7 @@ func (r *Registry) registryPath() string { // Load reads the plugin registry from disk. // If the registry file does not exist, the registry starts empty. +// Usage: Load(...) func (r *Registry) Load() error { path := r.registryPath() @@ -102,6 +107,7 @@ func (r *Registry) Load() error { } // Save writes the plugin registry to disk. +// Usage: Save(...) func (r *Registry) Save() error { if err := r.medium.EnsureDir(r.basePath); err != nil { return coreerr.E("plugin.Registry.Save", "failed to create plugin directory", err) diff --git a/plugin/registry_test.go b/plugin/registry_test.go index 853998b..75485c0 100644 --- a/plugin/registry_test.go +++ b/plugin/registry_test.go @@ -27,7 +27,7 @@ func TestRegistry_Add_Good(t *testing.T) { assert.Equal(t, "1.0.0", list[0].Version) } -func TestRegistry_Add_Bad_EmptyName(t *testing.T) { +func TestRegistry_Add_Bad_EmptyName_Good(t *testing.T) { m := io.NewMockMedium() reg := NewRegistry(m, "/home/user/.core/plugins") @@ -68,7 +68,7 @@ func TestRegistry_Get_Good(t *testing.T) { assert.Equal(t, "2.0.0", cfg.Version) } -func TestRegistry_Get_Bad_NotFound(t *testing.T) { +func TestRegistry_Get_Bad_NotFound_Good(t *testing.T) { m := io.NewMockMedium() reg := NewRegistry(m, "/home/user/.core/plugins") @@ -77,7 +77,7 @@ func TestRegistry_Get_Bad_NotFound(t *testing.T) { assert.Nil(t, cfg) } -func TestRegistry_Remove_Bad_NotFound(t *testing.T) { +func TestRegistry_Remove_Bad_NotFound_Good(t *testing.T) { m := io.NewMockMedium() reg := NewRegistry(m, "/home/user/.core/plugins") @@ -128,7 +128,7 @@ func TestRegistry_SaveLoad_Good(t *testing.T) { assert.False(t, b.Enabled) } -func TestRegistry_Load_Good_EmptyWhenNoFile(t *testing.T) { +func TestRegistry_Load_Good_EmptyWhenNoFile_Good(t *testing.T) { m := io.NewMockMedium() reg := NewRegistry(m, "/home/user/.core/plugins") @@ -137,7 +137,7 @@ func TestRegistry_Load_Good_EmptyWhenNoFile(t *testing.T) { assert.Empty(t, reg.List()) } -func TestRegistry_Load_Bad_InvalidJSON(t *testing.T) { +func TestRegistry_Load_Bad_InvalidJSON_Good(t *testing.T) { m := io.NewMockMedium() basePath := "/home/user/.core/plugins" _ = m.Write(basePath+"/registry.json", "not valid json {{{") @@ -148,7 +148,7 @@ func TestRegistry_Load_Bad_InvalidJSON(t *testing.T) { assert.Contains(t, err.Error(), "failed to parse registry") } -func TestRegistry_Load_Good_NullJSON(t *testing.T) { +func TestRegistry_Load_Good_NullJSON_Good(t *testing.T) { m := io.NewMockMedium() basePath := "/home/user/.core/plugins" _ = m.Write(basePath+"/registry.json", "null") @@ -159,7 +159,7 @@ func TestRegistry_Load_Good_NullJSON(t *testing.T) { assert.Empty(t, reg.List()) } -func TestRegistry_Save_Good_CreatesDir(t *testing.T) { +func TestRegistry_Save_Good_CreatesDir_Good(t *testing.T) { m := io.NewMockMedium() basePath := "/home/user/.core/plugins" reg := NewRegistry(m, basePath) @@ -172,7 +172,7 @@ func TestRegistry_Save_Good_CreatesDir(t *testing.T) { assert.True(t, m.IsFile(basePath+"/registry.json")) } -func TestRegistry_List_Good_Sorted(t *testing.T) { +func TestRegistry_List_Good_Sorted_Good(t *testing.T) { m := io.NewMockMedium() reg := NewRegistry(m, "/plugins") diff --git a/repos/gitstate_test.go b/repos/gitstate_test.go index 7e8c807..1c3a929 100644 --- a/repos/gitstate_test.go +++ b/repos/gitstate_test.go @@ -49,7 +49,7 @@ func TestGitState_LoadSave_Good(t *testing.T) { assert.Equal(t, []string{"core-php", "core-tenant"}, loaded.Agents["cladius"].Active) } -func TestGitState_Load_Good_NoFile(t *testing.T) { +func TestGitState_Load_Good_NoFile_Good(t *testing.T) { m := io.NewMockMedium() _ = m.EnsureDir("/workspace/.core") @@ -60,7 +60,7 @@ func TestGitState_Load_Good_NoFile(t *testing.T) { assert.Empty(t, gs.Agents) } -func TestGitState_Load_Bad_InvalidYAML(t *testing.T) { +func TestGitState_Load_Bad_InvalidYAML_Good(t *testing.T) { m := io.NewMockMedium() _ = m.Write("/workspace/.core/git.yaml", "{{{{not yaml") @@ -109,7 +109,7 @@ func TestGitState_UpdateRepo_Good(t *testing.T) { assert.Equal(t, 1, r.Behind) } -func TestGitState_UpdateRepo_Good_Overwrite(t *testing.T) { +func TestGitState_UpdateRepo_Good_Overwrite_Good(t *testing.T) { gs := NewGitState() gs.UpdateRepo("core-php", "main", "origin", 1, 0) gs.UpdateRepo("core-php", "main", "origin", 0, 0) @@ -131,7 +131,7 @@ func TestGitState_Heartbeat_Good(t *testing.T) { assert.True(t, agent.LastSeen.After(before) || agent.LastSeen.Equal(before)) } -func TestGitState_Heartbeat_Good_Updates(t *testing.T) { +func TestGitState_Heartbeat_Good_Updates_Good(t *testing.T) { gs := NewGitState() gs.Heartbeat("cladius", []string{"core-php"}) gs.Heartbeat("cladius", []string{"core-php", "core-tenant"}) @@ -157,7 +157,7 @@ func TestGitState_StaleAgents_Good(t *testing.T) { assert.NotContains(t, stale, "fresh") } -func TestGitState_StaleAgents_Good_NoneStale(t *testing.T) { +func TestGitState_StaleAgents_Good_NoneStale_Good(t *testing.T) { gs := NewGitState() gs.Heartbeat("cladius", []string{"core-php"}) @@ -177,7 +177,7 @@ func TestGitState_ActiveAgentsFor_Good(t *testing.T) { assert.NotContains(t, agents, "athena") } -func TestGitState_ActiveAgentsFor_Good_IgnoresStale(t *testing.T) { +func TestGitState_ActiveAgentsFor_Good_IgnoresStale_Good(t *testing.T) { gs := NewGitState() gs.Agents["gone"] = &AgentState{ LastSeen: time.Now().Add(-20 * time.Minute), @@ -188,7 +188,7 @@ func TestGitState_ActiveAgentsFor_Good_IgnoresStale(t *testing.T) { assert.Empty(t, agents) } -func TestGitState_ActiveAgentsFor_Good_NoMatch(t *testing.T) { +func TestGitState_ActiveAgentsFor_Good_NoMatch_Good(t *testing.T) { gs := NewGitState() gs.Heartbeat("cladius", []string{"core-php"}) @@ -198,18 +198,18 @@ func TestGitState_ActiveAgentsFor_Good_NoMatch(t *testing.T) { // ── NeedsPull ────────────────────────────────────────────────────── -func TestGitState_NeedsPull_Good_NeverPulled(t *testing.T) { +func TestGitState_NeedsPull_Good_NeverPulled_Good(t *testing.T) { gs := NewGitState() assert.True(t, gs.NeedsPull("core-php", 5*time.Minute)) } -func TestGitState_NeedsPull_Good_RecentPull(t *testing.T) { +func TestGitState_NeedsPull_Good_RecentPull_Good(t *testing.T) { gs := NewGitState() gs.TouchPull("core-php") assert.False(t, gs.NeedsPull("core-php", 5*time.Minute)) } -func TestGitState_NeedsPull_Good_StalePull(t *testing.T) { +func TestGitState_NeedsPull_Good_StalePull_Good(t *testing.T) { gs := NewGitState() gs.Repos["core-php"] = &RepoGitState{ LastPull: time.Now().Add(-10 * time.Minute), diff --git a/repos/kbconfig_test.go b/repos/kbconfig_test.go index c92a361..9e8bf56 100644 --- a/repos/kbconfig_test.go +++ b/repos/kbconfig_test.go @@ -47,7 +47,7 @@ func TestKBConfig_LoadSave_Good(t *testing.T) { assert.True(t, loaded.Wiki.Enabled) } -func TestKBConfig_Load_Good_NoFile(t *testing.T) { +func TestKBConfig_Load_Good_NoFile_Good(t *testing.T) { m := io.NewMockMedium() _ = m.EnsureDir("/workspace/.core") @@ -56,7 +56,7 @@ func TestKBConfig_Load_Good_NoFile(t *testing.T) { assert.Equal(t, DefaultKBConfig().Search.Collection, kb.Search.Collection) } -func TestKBConfig_Load_Good_PartialOverride(t *testing.T) { +func TestKBConfig_Load_Good_PartialOverride_Good(t *testing.T) { m := io.NewMockMedium() _ = m.Write("/workspace/.core/kb.yaml", ` version: 1 @@ -75,7 +75,7 @@ search: assert.Equal(t, "embeddinggemma", kb.Search.EmbedModel) } -func TestKBConfig_Load_Bad_InvalidYAML(t *testing.T) { +func TestKBConfig_Load_Bad_InvalidYAML_Good(t *testing.T) { m := io.NewMockMedium() _ = m.Write("/workspace/.core/kb.yaml", "{{{{broken") @@ -92,7 +92,7 @@ func TestKBConfig_WikiRepoURL_Good(t *testing.T) { assert.Equal(t, "ssh://git@forge.lthn.ai:2223/core/go-scm.wiki.git", url) } -func TestKBConfig_WikiRepoURL_Good_CustomRemote(t *testing.T) { +func TestKBConfig_WikiRepoURL_Good_CustomRemote_Good(t *testing.T) { kb := &KBConfig{ Wiki: WikiConfig{Remote: "ssh://git@git.example.com/org"}, } diff --git a/repos/registry_test.go b/repos/registry_test.go index 70cfc43..2c801c8 100644 --- a/repos/registry_test.go +++ b/repos/registry_test.go @@ -39,7 +39,7 @@ repos: assert.Equal(t, reg, repo.registry) } -func TestLoadRegistry_Good_WithDefaults(t *testing.T) { +func TestLoadRegistry_Good_WithDefaults_Good(t *testing.T) { m := io.NewMockMedium() yaml := ` version: 1 @@ -71,7 +71,7 @@ repos: assert.Equal(t, "github-actions", admin.CI) } -func TestLoadRegistry_Good_CustomRepoPath(t *testing.T) { +func TestLoadRegistry_Good_CustomRepoPath_Good(t *testing.T) { m := io.NewMockMedium() yaml := ` version: 1 @@ -92,7 +92,7 @@ repos: assert.Equal(t, "/opt/special-repo", repo.Path) } -func TestLoadRegistry_Good_CIOverride(t *testing.T) { +func TestLoadRegistry_Good_CIOverride_Good(t *testing.T) { m := io.NewMockMedium() yaml := ` version: 1 @@ -119,14 +119,14 @@ repos: assert.Equal(t, "custom-ci", b.CI) } -func TestLoadRegistry_Bad_FileNotFound(t *testing.T) { +func TestLoadRegistry_Bad_FileNotFound_Good(t *testing.T) { m := io.NewMockMedium() _, err := LoadRegistry(m, "/nonexistent/repos.yaml") assert.Error(t, err) assert.Contains(t, err.Error(), "failed to read") } -func TestLoadRegistry_Bad_InvalidYAML(t *testing.T) { +func TestLoadRegistry_Bad_InvalidYAML_Good(t *testing.T) { m := io.NewMockMedium() _ = m.Write("/tmp/bad.yaml", "{{{{not yaml at all") @@ -180,7 +180,7 @@ func TestRegistry_Get_Good(t *testing.T) { assert.Equal(t, "core-php", repo.Name) } -func TestRegistry_Get_Bad_NotFound(t *testing.T) { +func TestRegistry_Get_Bad_NotFound_Good(t *testing.T) { reg := newTestRegistry(t) _, ok := reg.Get("nonexistent") assert.False(t, ok) @@ -200,7 +200,7 @@ func TestRegistry_ByType_Good(t *testing.T) { assert.Len(t, products, 1) } -func TestRegistry_ByType_Good_NoMatch(t *testing.T) { +func TestRegistry_ByType_Good_NoMatch_Good(t *testing.T) { reg := newTestRegistry(t) templates := reg.ByType("template") assert.Empty(t, templates) @@ -242,7 +242,7 @@ func TopologicalOrder(reg *Registry) ([]*Repo, error) { return reg.TopologicalOrder() } -func TestTopologicalOrder_Bad_CircularDep(t *testing.T) { +func TestTopologicalOrder_Bad_CircularDep_Good(t *testing.T) { m := io.NewMockMedium() yaml := ` version: 1 @@ -265,7 +265,7 @@ repos: assert.Contains(t, err.Error(), "circular dependency") } -func TestTopologicalOrder_Bad_UnknownDep(t *testing.T) { +func TestTopologicalOrder_Bad_UnknownDep_Good(t *testing.T) { m := io.NewMockMedium() yaml := ` version: 1 @@ -285,7 +285,7 @@ repos: assert.Contains(t, err.Error(), "unknown repo") } -func TestTopologicalOrder_Good_NoDeps(t *testing.T) { +func TestTopologicalOrder_Good_NoDeps_Good(t *testing.T) { m := io.NewMockMedium() yaml := ` version: 1 @@ -333,7 +333,7 @@ func TestScanDirectory_Good(t *testing.T) { assert.False(t, ok) } -func TestScanDirectory_Good_DetectsGitHubOrg(t *testing.T) { +func TestScanDirectory_Good_DetectsGitHubOrg_Good(t *testing.T) { m := io.NewMockMedium() _ = m.EnsureDir("/workspace/my-repo/.git") @@ -349,7 +349,7 @@ func TestScanDirectory_Good_DetectsGitHubOrg(t *testing.T) { assert.Equal(t, "host-uk", reg.Org) } -func TestScanDirectory_Good_DetectsHTTPSOrg(t *testing.T) { +func TestScanDirectory_Good_DetectsHTTPSOrg_Good(t *testing.T) { m := io.NewMockMedium() _ = m.EnsureDir("/workspace/my-repo/.git") @@ -362,7 +362,7 @@ func TestScanDirectory_Good_DetectsHTTPSOrg(t *testing.T) { assert.Equal(t, "lethean-io", reg.Org) } -func TestScanDirectory_Good_EmptyDir(t *testing.T) { +func TestScanDirectory_Good_EmptyDir_Good(t *testing.T) { m := io.NewMockMedium() _ = m.EnsureDir("/empty") @@ -372,7 +372,7 @@ func TestScanDirectory_Good_EmptyDir(t *testing.T) { assert.Equal(t, "", reg.Org) } -func TestScanDirectory_Bad_InvalidDir(t *testing.T) { +func TestScanDirectory_Bad_InvalidDir_Good(t *testing.T) { m := io.NewMockMedium() _, err := ScanDirectory(m, "/nonexistent") assert.Error(t, err) @@ -381,7 +381,7 @@ func TestScanDirectory_Bad_InvalidDir(t *testing.T) { // ── detectOrg ────────────────────────────────────────────────────── -func TestDetectOrg_Good_SSHRemote(t *testing.T) { +func TestDetectOrg_Good_SSHRemote_Good(t *testing.T) { m := io.NewMockMedium() _ = m.Write("/repo/.git/config", `[remote "origin"] url = git@github.com:host-uk/core.git @@ -389,7 +389,7 @@ func TestDetectOrg_Good_SSHRemote(t *testing.T) { assert.Equal(t, "host-uk", detectOrg(m, "/repo")) } -func TestDetectOrg_Good_HTTPSRemote(t *testing.T) { +func TestDetectOrg_Good_HTTPSRemote_Good(t *testing.T) { m := io.NewMockMedium() _ = m.Write("/repo/.git/config", `[remote "origin"] url = https://github.com/snider/project.git @@ -397,12 +397,12 @@ func TestDetectOrg_Good_HTTPSRemote(t *testing.T) { assert.Equal(t, "snider", detectOrg(m, "/repo")) } -func TestDetectOrg_Bad_NoConfig(t *testing.T) { +func TestDetectOrg_Bad_NoConfig_Good(t *testing.T) { m := io.NewMockMedium() assert.Equal(t, "", detectOrg(m, "/nonexistent")) } -func TestDetectOrg_Bad_NoRemote(t *testing.T) { +func TestDetectOrg_Bad_NoRemote_Good(t *testing.T) { m := io.NewMockMedium() _ = m.Write("/repo/.git/config", `[core] repositoryformatversion = 0 @@ -410,7 +410,7 @@ func TestDetectOrg_Bad_NoRemote(t *testing.T) { assert.Equal(t, "", detectOrg(m, "/repo")) } -func TestDetectOrg_Bad_NonGitHubRemote(t *testing.T) { +func TestDetectOrg_Bad_NonGitHubRemote_Good(t *testing.T) { m := io.NewMockMedium() _ = m.Write("/repo/.git/config", `[remote "origin"] url = ssh://git@forge.lthn.ai:2223/core/go.git @@ -420,13 +420,13 @@ func TestDetectOrg_Bad_NonGitHubRemote(t *testing.T) { // ── expandPath ───────────────────────────────────────────────────── -func TestExpandPath_Good_Tilde(t *testing.T) { +func TestExpandPath_Good_Tilde_Good(t *testing.T) { got := expandPath("~/Code/repos") assert.NotContains(t, got, "~") assert.Contains(t, got, "Code/repos") } -func TestExpandPath_Good_NoTilde(t *testing.T) { +func TestExpandPath_Good_NoTilde_Good(t *testing.T) { assert.Equal(t, "/absolute/path", expandPath("/absolute/path")) assert.Equal(t, "relative/path", expandPath("relative/path")) } @@ -473,14 +473,14 @@ func TestRepo_IsGitRepo_Good(t *testing.T) { // ── getMedium fallback ───────────────────────────────────────────── -func TestGetMedium_Good_FallbackToLocal(t *testing.T) { +func TestGetMedium_Good_FallbackToLocal_Good(t *testing.T) { repo := &Repo{Name: "orphan", Path: "/tmp/orphan"} // No registry set — should fall back to io.Local. m := repo.getMedium() assert.Equal(t, io.Local, m) } -func TestGetMedium_Good_NilMediumFallback(t *testing.T) { +func TestGetMedium_Good_NilMediumFallback_Good(t *testing.T) { reg := &Registry{} // medium is nil. repo := &Repo{Name: "test", registry: reg} m := repo.getMedium() diff --git a/repos/workconfig_test.go b/repos/workconfig_test.go index 3be7066..f3e9a6e 100644 --- a/repos/workconfig_test.go +++ b/repos/workconfig_test.go @@ -50,7 +50,7 @@ func TestWorkConfig_LoadSave_Good(t *testing.T) { assert.True(t, loaded.Sync.AutoPull) } -func TestWorkConfig_Load_Good_NoFile(t *testing.T) { +func TestWorkConfig_Load_Good_NoFile_Good(t *testing.T) { m := io.NewMockMedium() _ = m.EnsureDir("/workspace/.core") @@ -59,7 +59,7 @@ func TestWorkConfig_Load_Good_NoFile(t *testing.T) { assert.Equal(t, DefaultWorkConfig().Sync.Interval, wc.Sync.Interval) } -func TestWorkConfig_Load_Good_PartialOverride(t *testing.T) { +func TestWorkConfig_Load_Good_PartialOverride_Good(t *testing.T) { m := io.NewMockMedium() _ = m.Write("/workspace/.core/work.yaml", ` version: 1 @@ -78,7 +78,7 @@ sync: assert.True(t, wc.Sync.CloneMissing) } -func TestWorkConfig_Load_Bad_InvalidYAML(t *testing.T) { +func TestWorkConfig_Load_Bad_InvalidYAML_Good(t *testing.T) { m := io.NewMockMedium() _ = m.Write("/workspace/.core/work.yaml", "{{{{broken") @@ -96,12 +96,12 @@ func TestWorkConfig_HasTrigger_Good(t *testing.T) { assert.True(t, wc.HasTrigger("scheduled")) } -func TestWorkConfig_HasTrigger_Bad_NotFound(t *testing.T) { +func TestWorkConfig_HasTrigger_Bad_NotFound_Good(t *testing.T) { wc := DefaultWorkConfig() assert.False(t, wc.HasTrigger("on_deploy")) } -func TestWorkConfig_HasTrigger_Good_CustomTriggers(t *testing.T) { +func TestWorkConfig_HasTrigger_Good_CustomTriggers_Good(t *testing.T) { wc := &WorkConfig{ Version: 1, Triggers: []string{"on_pr", "manual"},