diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ca1d69e3..d63c43c4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,11 +28,12 @@ jobs: - name: Run tests with coverage run: | - # Run tests on all packages with race detection - go test -v -race ./... + # Run tests on all packages with race detection and parallel execution + go test -v -race -parallel 8 ./... # Calculate coverage only for pkg/ (cmd/ is CLI code with lower test coverage) # Use -count=1 to disable test caching and get accurate coverage - go test -count=1 -coverprofile=coverage.out -covermode=atomic ./pkg/... + # Use -parallel 8 for faster execution (4.7x faster than sequential) + go test -count=1 -parallel 8 -coverprofile=coverage.out -covermode=atomic ./pkg/... go tool cover -html=coverage.out -o coverage.html - name: Calculate coverage diff --git a/cmd/auth.go b/cmd/auth.go index 88b6ca44..400a7c05 100644 --- a/cmd/auth.go +++ b/cmd/auth.go @@ -197,7 +197,7 @@ SCOPES REQUESTED: • Cases and usage data See: pup auth --help for complete scope list`, - RunE: runAuthLogin, + RunE: runAuthLogin, } var authStatusCmd = &cobra.Command{ @@ -266,7 +266,7 @@ REFRESH TOKEN: The refresh token (valid for 30 days) is used to obtain new access tokens without requiring a new browser login. If the refresh token expires, you'll need to run 'pup auth login' again.`, - RunE: runAuthStatus, + RunE: runAuthStatus, } var authLogoutCmd = &cobra.Command{ diff --git a/cmd/cloud_test.go b/cmd/cloud_test.go index dbe4c7ee..77e45b1a 100644 --- a/cmd/cloud_test.go +++ b/cmd/cloud_test.go @@ -138,10 +138,10 @@ func TestCloudAzureCmd(t *testing.T) { func TestCloudCmd_CommandHierarchy(t *testing.T) { tests := []struct { - name string - parentCmd *cobra.Command - parentUse string - subcommandUse string + name string + parentCmd *cobra.Command + parentUse string + subcommandUse string }{ { name: "AWS subcommand", diff --git a/cmd/dashboards.go b/cmd/dashboards.go index 99fe919e..cbb8bb48 100644 --- a/cmd/dashboards.go +++ b/cmd/dashboards.go @@ -112,7 +112,7 @@ FILTERING: SORTING: Dashboards are returned sorted by popularity (most viewed first).`, - RunE: runDashboardsList, + RunE: runDashboardsList, } var dashboardsGetCmd = &cobra.Command{ @@ -179,8 +179,8 @@ USE CASES: • Version control dashboard definitions • Programmatic dashboard generation • Extract widget configurations for reuse`, - Args: cobra.ExactArgs(1), - RunE: runDashboardsGet, + Args: cobra.ExactArgs(1), + RunE: runDashboardsGet, } var dashboardsDeleteCmd = &cobra.Command{ diff --git a/cmd/dashboards_test.go b/cmd/dashboards_test.go index 64c4b183..e8a7995a 100644 --- a/cmd/dashboards_test.go +++ b/cmd/dashboards_test.go @@ -109,9 +109,9 @@ func TestRunDashboardsGet(t *testing.T) { defer cleanup() tests := []struct { - name string - dashboardID string - wantErr bool + name string + dashboardID string + wantErr bool }{ { name: "with valid dashboard ID", diff --git a/cmd/error_tracking_test.go b/cmd/error_tracking_test.go index b7d4b408..f7769fd5 100644 --- a/cmd/error_tracking_test.go +++ b/cmd/error_tracking_test.go @@ -170,5 +170,3 @@ func TestErrorTrackingCmd_CommandHierarchy(t *testing.T) { } } } - - diff --git a/cmd/incidents.go b/cmd/incidents.go index 83e90ddc..845b2769 100644 --- a/cmd/incidents.go +++ b/cmd/incidents.go @@ -133,7 +133,7 @@ FILTERING: SORTING: Incidents are returned sorted by creation time (most recent first).`, - RunE: runIncidentsList, + RunE: runIncidentsList, } var incidentsGetCmd = &cobra.Command{ diff --git a/cmd/incidents_test.go b/cmd/incidents_test.go index 5cd86a85..635717c5 100644 --- a/cmd/incidents_test.go +++ b/cmd/incidents_test.go @@ -82,9 +82,9 @@ func TestRunIncidentsGet(t *testing.T) { defer cleanup() tests := []struct { - name string - incidentID string - wantErr bool + name string + incidentID string + wantErr bool }{ { name: "with valid incident ID", diff --git a/cmd/integrations_test.go b/cmd/integrations_test.go index 66113373..8f0a21c4 100644 --- a/cmd/integrations_test.go +++ b/cmd/integrations_test.go @@ -138,10 +138,10 @@ func TestIntegrationsWebhooksCmd(t *testing.T) { func TestIntegrationsCmd_CommandHierarchy(t *testing.T) { tests := []struct { - name string - parentCmd *cobra.Command - parentUse string - subcommandUse string + name string + parentCmd *cobra.Command + parentUse string + subcommandUse string }{ { name: "Slack subcommand", diff --git a/cmd/metrics.go b/cmd/metrics.go index 4f302642..90fe7c2e 100644 --- a/cmd/metrics.go +++ b/cmd/metrics.go @@ -959,4 +959,3 @@ func runMetricsTagsList(cmd *cobra.Command, args []string) error { // NOTE: ListTagsByMetricName is not available in datadog-api-client-go v2.30.0 return fmt.Errorf("listing tags by metric name is not supported in the current API client version") } - diff --git a/cmd/metrics_test.go b/cmd/metrics_test.go index dda66a05..28e2cf53 100644 --- a/cmd/metrics_test.go +++ b/cmd/metrics_test.go @@ -65,19 +65,19 @@ func TestRunMetricsSearch(t *testing.T) { defer cleanup() tests := []struct { - name string - query string - from string - to string - wantErr bool + name string + query string + from string + to string + wantErr bool wantErrContains string }{ { - name: "fails on client creation", - query: "avg:system.cpu.user{*}", - from: "1h", - to: "now", - wantErr: true, + name: "fails on client creation", + query: "avg:system.cpu.user{*}", + from: "1h", + to: "now", + wantErr: true, wantErrContains: "mock client", }, } @@ -110,19 +110,19 @@ func TestRunMetricsQuery(t *testing.T) { defer cleanup() tests := []struct { - name string - query string - from string - to string - wantErr bool + name string + query string + from string + to string + wantErr bool wantErrContains string }{ { - name: "fails on client creation", - query: "avg:system.cpu.user{*}", - from: "1h", - to: "now", - wantErr: true, + name: "fails on client creation", + query: "avg:system.cpu.user{*}", + from: "1h", + to: "now", + wantErr: true, wantErrContains: "mock client", }, } @@ -474,15 +474,15 @@ func TestRunMetricsMetadataGet(t *testing.T) { defer cleanup() tests := []struct { - name string - metricName string - wantErr bool + name string + metricName string + wantErr bool wantErrContains string }{ { - name: "fails on client creation", - metricName: "system.cpu.user", - wantErr: true, + name: "fails on client creation", + metricName: "system.cpu.user", + wantErr: true, wantErrContains: "mock client", }, } @@ -511,39 +511,39 @@ func TestRunMetricsMetadataUpdate(t *testing.T) { defer cleanup() tests := []struct { - name string - metricName string - description string - unit string - metricType string - wantErr bool + name string + metricName string + description string + unit string + metricType string + wantErr bool wantErrContains string }{ { - name: "update description", - metricName: "system.cpu.user", - description: "CPU user time", - unit: "", - metricType: "", - wantErr: true, + name: "update description", + metricName: "system.cpu.user", + description: "CPU user time", + unit: "", + metricType: "", + wantErr: true, wantErrContains: "mock client", }, { - name: "update multiple fields", - metricName: "system.cpu.user", - description: "CPU user time", - unit: "percent", - metricType: "gauge", - wantErr: true, + name: "update multiple fields", + metricName: "system.cpu.user", + description: "CPU user time", + unit: "percent", + metricType: "gauge", + wantErr: true, wantErrContains: "mock client", }, { - name: "no fields specified hits client error first", - metricName: "system.cpu.user", - description: "", - unit: "", - metricType: "", - wantErr: true, + name: "no fields specified hits client error first", + metricName: "system.cpu.user", + description: "", + unit: "", + metricType: "", + wantErr: true, wantErrContains: "mock client", }, } diff --git a/cmd/misc_test.go b/cmd/misc_test.go index dadd1057..936424ca 100644 --- a/cmd/misc_test.go +++ b/cmd/misc_test.go @@ -92,12 +92,12 @@ func TestMiscStatusCmd(t *testing.T) { func TestMiscCmd_CommandStructure(t *testing.T) { tests := []struct { - name string - cmd *cobra.Command - wantUse string - wantShort bool - wantRunE bool - wantArgs bool + name string + cmd *cobra.Command + wantUse string + wantShort bool + wantRunE bool + wantArgs bool }{ { name: "ip-ranges command", diff --git a/cmd/miscellaneous.go b/cmd/miscellaneous.go index cd392274..22b93091 100644 --- a/cmd/miscellaneous.go +++ b/cmd/miscellaneous.go @@ -76,7 +76,7 @@ func runMiscIPRanges(cmd *cobra.Command, args []string) error { func runMiscStatus(cmd *cobra.Command, args []string) error { result := map[string]interface{}{ - "status": "ok", + "status": "ok", "message": "API is operational", } diff --git a/cmd/monitors.go b/cmd/monitors.go index c61f88a8..27dacf54 100644 --- a/cmd/monitors.go +++ b/cmd/monitors.go @@ -239,13 +239,13 @@ EXAMPLES: } var ( - monitorName string - monitorTags string - monitorLimit int32 - searchQuery string - searchPage int64 - searchPerPage int64 - searchSort string + monitorName string + monitorTags string + monitorLimit int32 + searchQuery string + searchPage int64 + searchPerPage int64 + searchSort string ) func init() { diff --git a/cmd/root.go b/cmd/root.go index 4d102b80..12d79382 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -35,7 +35,7 @@ var ( autoApprove bool // Dependency injection points for testing - clientFactory = defaultClientFactory + clientFactory = defaultClientFactory outputWriter io.Writer = os.Stdout inputReader io.Reader = os.Stdin ) diff --git a/cmd/root_test.go b/cmd/root_test.go index adeee021..2c084341 100644 --- a/cmd/root_test.go +++ b/cmd/root_test.go @@ -205,8 +205,8 @@ func TestFormatAPIError_AllStatusCodes(t *testing.T) { {502, true, "Datadog API is experiencing issues"}, {503, true, "Datadog API is experiencing issues"}, {504, true, "Datadog API is experiencing issues"}, - {200, false, ""}, // Should just show basic error - {201, false, ""}, // Should just show basic error + {200, false, ""}, // Should just show basic error + {201, false, ""}, // Should just show basic error {418, true, "Invalid request"}, // Other 4xx } diff --git a/cmd/slos.go b/cmd/slos.go index 216ef806..52982b28 100644 --- a/cmd/slos.go +++ b/cmd/slos.go @@ -125,7 +125,7 @@ FILTERING: • Breaching SLOs: pup slos list | jq '.data[] | select(.status.state == "breaching")' • High error budget: pup slos list | jq '.data[] | select(.status.error_budget_remaining > 50)' • By tag: pup slos list | jq '.data[] | select(.tags[] | contains("team:backend"))'`, - RunE: runSlosList, + RunE: runSlosList, } var slosGetCmd = &cobra.Command{ @@ -197,8 +197,8 @@ USE CASES: • Analyze historical SLO performance • Track error budget burn rate • Report on service reliability`, - Args: cobra.ExactArgs(1), - RunE: runSlosGet, + Args: cobra.ExactArgs(1), + RunE: runSlosGet, } var slosDeleteCmd = &cobra.Command{ diff --git a/pkg/auth/callback/server.go b/pkg/auth/callback/server.go index 905af6ea..472b785f 100644 --- a/pkg/auth/callback/server.go +++ b/pkg/auth/callback/server.go @@ -17,9 +17,9 @@ import ( // CallbackResult represents the result of the OAuth callback type CallbackResult struct { - Code string - State string - Error string + Code string + State string + Error string ErrorDescription string } diff --git a/pkg/auth/callback/server_test.go b/pkg/auth/callback/server_test.go index 4c734eac..59e1bdf3 100644 --- a/pkg/auth/callback/server_test.go +++ b/pkg/auth/callback/server_test.go @@ -15,6 +15,7 @@ import ( ) func TestNewServer(t *testing.T) { + t.Parallel() server, err := NewServer() if err != nil { t.Fatalf("NewServer() error = %v", err) @@ -45,6 +46,7 @@ func TestNewServer(t *testing.T) { } func TestServer_Port(t *testing.T) { + t.Parallel() server, err := NewServer() if err != nil { t.Fatalf("NewServer() error = %v", err) @@ -70,6 +72,7 @@ func TestServer_Port(t *testing.T) { } func TestServer_RedirectURI(t *testing.T) { + t.Parallel() server, err := NewServer() if err != nil { t.Fatalf("NewServer() error = %v", err) @@ -96,6 +99,7 @@ func TestServer_RedirectURI(t *testing.T) { } func TestServer_StartStop(t *testing.T) { + // NOTE: Not parallel - binds to network port server, err := NewServer() if err != nil { t.Fatalf("NewServer() error = %v", err) @@ -131,6 +135,7 @@ func TestServer_StartStop(t *testing.T) { } func TestServer_Stop_WhenNotStarted(t *testing.T) { + t.Parallel() server, err := NewServer() if err != nil { t.Fatalf("NewServer() error = %v", err) @@ -143,6 +148,7 @@ func TestServer_Stop_WhenNotStarted(t *testing.T) { } func TestServer_WaitForCallback_Success(t *testing.T) { + // NOTE: Not parallel - binds to network port server, err := NewServer() if err != nil { t.Fatalf("NewServer() error = %v", err) @@ -185,6 +191,7 @@ func TestServer_WaitForCallback_Success(t *testing.T) { } func TestServer_WaitForCallback_Error(t *testing.T) { + // NOTE: Not parallel - binds to network port server, err := NewServer() if err != nil { t.Fatalf("NewServer() error = %v", err) @@ -227,6 +234,7 @@ func TestServer_WaitForCallback_Error(t *testing.T) { } func TestServer_WaitForCallback_Timeout(t *testing.T) { + // NOTE: Not parallel - binds to network port server, err := NewServer() if err != nil { t.Fatalf("NewServer() error = %v", err) @@ -248,6 +256,7 @@ func TestServer_WaitForCallback_Timeout(t *testing.T) { } func TestServer_HandleCallback_SuccessResponse(t *testing.T) { + t.Parallel() server := &Server{ port: 8000, resultCh: make(chan CallbackResult, 1), @@ -287,6 +296,7 @@ func TestServer_HandleCallback_SuccessResponse(t *testing.T) { } func TestServer_HandleCallback_ErrorResponse(t *testing.T) { + t.Parallel() server := &Server{ port: 8000, resultCh: make(chan CallbackResult, 1), @@ -326,6 +336,7 @@ func TestServer_HandleCallback_ErrorResponse(t *testing.T) { } func TestServer_RenderSuccess(t *testing.T) { + t.Parallel() server := &Server{port: 8000} w := httptest.NewRecorder() @@ -353,6 +364,7 @@ func TestServer_RenderSuccess(t *testing.T) { } func TestServer_RenderError(t *testing.T) { + t.Parallel() server := &Server{port: 8000} w := httptest.NewRecorder() @@ -386,6 +398,7 @@ func TestServer_RenderError(t *testing.T) { } func TestServer_RenderError_NoDescription(t *testing.T) { + t.Parallel() server := &Server{port: 8000} w := httptest.NewRecorder() @@ -402,6 +415,7 @@ func TestServer_RenderError_NoDescription(t *testing.T) { } func TestDCRRedirectPorts(t *testing.T) { + t.Parallel() // Verify DCRRedirectPorts matches TypeScript PR #84 expected := []int{8000, 8080, 8888, 9000} diff --git a/pkg/auth/dcr/client_test.go b/pkg/auth/dcr/client_test.go index 091acce5..2cf624c2 100644 --- a/pkg/auth/dcr/client_test.go +++ b/pkg/auth/dcr/client_test.go @@ -30,6 +30,7 @@ func (m *mockTransport) RoundTrip(req *http.Request) (*http.Response, error) { } func TestNewClient(t *testing.T) { + t.Parallel() client := NewClient("datadoghq.com") if client == nil { t.Fatal("NewClient() returned nil") @@ -45,6 +46,7 @@ func TestNewClient(t *testing.T) { } func TestGetRedirectURIs(t *testing.T) { + t.Parallel() uris := GetRedirectURIs() expectedURIs := []string{ @@ -66,6 +68,7 @@ func TestGetRedirectURIs(t *testing.T) { } func TestClient_Register_Success(t *testing.T) { + t.Parallel() // Create mock server server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Verify request method and path @@ -142,6 +145,7 @@ func TestClient_Register_Success(t *testing.T) { } func TestClient_Register_HTTPError(t *testing.T) { + t.Parallel() // Create mock server that returns error server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusBadRequest) @@ -164,6 +168,7 @@ func TestClient_Register_HTTPError(t *testing.T) { } func TestClient_Register_OAuthError(t *testing.T) { + t.Parallel() // Create mock server that returns OAuth error server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { oauthErr := types.OAuthError{ @@ -190,6 +195,7 @@ func TestClient_Register_OAuthError(t *testing.T) { } func TestClient_ExchangeCode_Success(t *testing.T) { + t.Parallel() // Create mock server server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Verify request method and path @@ -275,6 +281,7 @@ func TestClient_ExchangeCode_Success(t *testing.T) { } func TestClient_ExchangeCode_Error(t *testing.T) { + t.Parallel() // Create mock server that returns error server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { oauthErr := types.OAuthError{ @@ -306,6 +313,7 @@ func TestClient_ExchangeCode_Error(t *testing.T) { } func TestClient_RefreshToken_Success(t *testing.T) { + t.Parallel() // Create mock server server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Verify request method @@ -375,6 +383,7 @@ func TestClient_RefreshToken_Success(t *testing.T) { } func TestClient_RefreshToken_Error(t *testing.T) { + t.Parallel() // Create mock server that returns error server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { oauthErr := types.OAuthError{ @@ -406,6 +415,7 @@ func TestClient_RefreshToken_Error(t *testing.T) { } func TestClient_RequestTokens_NetworkError(t *testing.T) { + t.Parallel() // Create client with invalid URL to simulate network error client := NewClient("invalid.example.com") @@ -421,6 +431,7 @@ func TestClient_RequestTokens_NetworkError(t *testing.T) { } func TestClient_RequestTokens_InvalidJSON(t *testing.T) { + t.Parallel() // Create mock server that returns invalid JSON server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) diff --git a/pkg/auth/oauth/client_test.go b/pkg/auth/oauth/client_test.go index fa3146b2..d4fd76a9 100644 --- a/pkg/auth/oauth/client_test.go +++ b/pkg/auth/oauth/client_test.go @@ -13,6 +13,7 @@ import ( ) func TestNewClient(t *testing.T) { + t.Parallel() client := NewClient("datadoghq.com") if client == nil { t.Fatal("NewClient() returned nil") @@ -20,6 +21,7 @@ func TestNewClient(t *testing.T) { } func TestClient_BuildAuthorizationURL(t *testing.T) { + t.Parallel() client := NewClient("datadoghq.com") challenge := &PKCEChallenge{ @@ -69,6 +71,7 @@ func TestClient_BuildAuthorizationURL(t *testing.T) { } func TestClient_BuildAuthorizationURL_DifferentSites(t *testing.T) { + t.Parallel() tests := []struct { name string site string @@ -98,6 +101,7 @@ func TestClient_BuildAuthorizationURL_DifferentSites(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + t.Parallel() client := NewClient(tt.site) challenge := &PKCEChallenge{ Challenge: "test", @@ -120,6 +124,7 @@ func TestClient_BuildAuthorizationURL_DifferentSites(t *testing.T) { } func TestClient_BuildAuthorizationURL_WithDefaultScopes(t *testing.T) { + t.Parallel() client := NewClient("datadoghq.com") challenge := &PKCEChallenge{ Challenge: "test-challenge", @@ -141,6 +146,7 @@ func TestClient_BuildAuthorizationURL_WithDefaultScopes(t *testing.T) { } func TestClient_ValidateCallback(t *testing.T) { + t.Parallel() client := NewClient("datadoghq.com") tests := []struct { @@ -186,6 +192,7 @@ func TestClient_ValidateCallback(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + t.Parallel() err := client.ValidateCallback(tt.code, tt.state, tt.expectedState) if tt.wantError { @@ -206,6 +213,7 @@ func TestClient_ValidateCallback(t *testing.T) { } func TestClient_ParseCallbackError(t *testing.T) { + t.Parallel() client := NewClient("datadoghq.com") tests := []struct { @@ -246,6 +254,7 @@ func TestClient_ParseCallbackError(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + t.Parallel() err := client.ParseCallbackError(tt.errorCode, tt.errorDescription) if tt.wantError { @@ -266,6 +275,7 @@ func TestClient_ParseCallbackError(t *testing.T) { } func TestClient_GetAuthConfig(t *testing.T) { + t.Parallel() client := NewClient("datadoghq.com") tests := []struct { @@ -292,6 +302,7 @@ func TestClient_GetAuthConfig(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + t.Parallel() config := client.GetAuthConfig(tt.scopes) if config == nil { diff --git a/pkg/auth/oauth/pkce_test.go b/pkg/auth/oauth/pkce_test.go index e570630b..8e5c801a 100644 --- a/pkg/auth/oauth/pkce_test.go +++ b/pkg/auth/oauth/pkce_test.go @@ -13,6 +13,7 @@ import ( ) func TestGeneratePKCEChallenge(t *testing.T) { + t.Parallel() challenge, err := GeneratePKCEChallenge() if err != nil { t.Fatalf("GeneratePKCEChallenge() error = %v", err) @@ -55,6 +56,7 @@ func TestGeneratePKCEChallenge(t *testing.T) { } func TestGeneratePKCEChallenge_Uniqueness(t *testing.T) { + t.Parallel() // Generate multiple challenges and ensure they're unique challenges := make(map[string]bool) @@ -76,6 +78,7 @@ func TestGeneratePKCEChallenge_Uniqueness(t *testing.T) { } func TestGenerateState(t *testing.T) { + t.Parallel() state, err := GenerateState() if err != nil { t.Fatalf("GenerateState() error = %v", err) @@ -94,6 +97,7 @@ func TestGenerateState(t *testing.T) { } func TestGenerateState_Uniqueness(t *testing.T) { + t.Parallel() // Generate multiple states and ensure they're unique states := make(map[string]bool) @@ -115,6 +119,7 @@ func TestGenerateState_Uniqueness(t *testing.T) { } func TestPKCEChallenge_RFC7636Compliance(t *testing.T) { + t.Parallel() // Test that PKCE implementation complies with RFC 7636 challenge, err := GeneratePKCEChallenge() if err != nil { @@ -147,6 +152,7 @@ func TestPKCEChallenge_RFC7636Compliance(t *testing.T) { } func TestGenerateRandomString(t *testing.T) { + t.Parallel() tests := []struct { name string length int @@ -160,6 +166,7 @@ func TestGenerateRandomString(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + t.Parallel() str, err := generateRandomString(tt.length) if err != nil { t.Fatalf("generateRandomString(%d) error = %v", tt.length, err) diff --git a/pkg/auth/storage/factory_test.go b/pkg/auth/storage/factory_test.go index 2312b2e8..6f844308 100644 --- a/pkg/auth/storage/factory_test.go +++ b/pkg/auth/storage/factory_test.go @@ -11,6 +11,7 @@ import ( ) func TestGetStorage(t *testing.T) { + t.Parallel() tests := []struct { name string envValue string @@ -88,6 +89,7 @@ func TestGetStorage(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + t.Parallel() // Reset storage state ResetStorage() @@ -147,6 +149,7 @@ func TestGetStorage(t *testing.T) { } func TestGetActiveBackend(t *testing.T) { + t.Parallel() // Reset storage state ResetStorage() @@ -171,6 +174,7 @@ func TestGetActiveBackend(t *testing.T) { } func TestIsUsingSecureStorage(t *testing.T) { + t.Parallel() tests := []struct { name string backend BackendType @@ -190,6 +194,7 @@ func TestIsUsingSecureStorage(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + t.Parallel() ResetStorage() // Force the backend type we want to test @@ -215,6 +220,7 @@ func TestIsUsingSecureStorage(t *testing.T) { } func TestGetStorageDescription(t *testing.T) { + t.Parallel() ResetStorage() // Force file backend @@ -240,6 +246,7 @@ func TestGetStorageDescription(t *testing.T) { } func TestResetStorage(t *testing.T) { + t.Parallel() // Get storage to initialize state _, err := GetStorage(&StorageOptions{ForceBackend: BackendFile}) if err != nil { @@ -261,6 +268,7 @@ func TestResetStorage(t *testing.T) { } func TestGetStorageDescription_Keychain(t *testing.T) { + t.Parallel() // Skip if keychain is not available if !IsKeychainAvailable() { t.Skip("Keychain not available in test environment") @@ -287,6 +295,7 @@ func TestGetStorageDescription_Keychain(t *testing.T) { } func TestGetStorageDescription_File(t *testing.T) { + t.Parallel() ResetStorage() // Force file backend @@ -308,6 +317,7 @@ func TestGetStorageDescription_File(t *testing.T) { } func TestDetectBackend_AutoFallback(t *testing.T) { + t.Parallel() // This tests the auto-detect fallback warning path ResetStorage() @@ -328,6 +338,7 @@ func TestDetectBackend_AutoFallback(t *testing.T) { } func TestDetectBackend_InvalidEnvValue(t *testing.T) { + t.Parallel() ResetStorage() // Set invalid environment value @@ -345,6 +356,7 @@ func TestDetectBackend_InvalidEnvValue(t *testing.T) { } func TestCreateStorage_UnknownBackend(t *testing.T) { + t.Parallel() ResetStorage() // Try to create storage with unknown backend @@ -359,6 +371,7 @@ func TestCreateStorage_UnknownBackend(t *testing.T) { } func TestGetStorage_CachedInstance(t *testing.T) { + t.Parallel() ResetStorage() // Get storage first time @@ -380,6 +393,7 @@ func TestGetStorage_CachedInstance(t *testing.T) { } func TestGetStorage_ForceBackendOverridesCache(t *testing.T) { + t.Parallel() ResetStorage() // Get file storage first diff --git a/pkg/auth/storage/keychain_test.go b/pkg/auth/storage/keychain_test.go index c82b5142..232ac008 100644 --- a/pkg/auth/storage/keychain_test.go +++ b/pkg/auth/storage/keychain_test.go @@ -16,6 +16,7 @@ import ( ) func TestKeychainStorage_GetBackendType(t *testing.T) { + t.Parallel() // Skip if keychain is not available if !IsKeychainAvailable() { t.Skip("Keychain not available in test environment") @@ -32,6 +33,7 @@ func TestKeychainStorage_GetBackendType(t *testing.T) { } func TestKeychainStorage_GetStorageLocation(t *testing.T) { + t.Parallel() // Skip if keychain is not available if !IsKeychainAvailable() { t.Skip("Keychain not available in test environment") @@ -65,6 +67,7 @@ func TestKeychainStorage_GetStorageLocation(t *testing.T) { } func TestKeychainStorage_TokenOperations(t *testing.T) { + t.Parallel() // Skip if keychain is not available if !IsKeychainAvailable() { t.Skip("Keychain not available in test environment") @@ -148,6 +151,7 @@ func TestKeychainStorage_TokenOperations(t *testing.T) { } func TestKeychainStorage_ClientCredentialOperations(t *testing.T) { + t.Parallel() // Skip if keychain is not available if !IsKeychainAvailable() { t.Skip("Keychain not available in test environment") @@ -230,6 +234,7 @@ func TestKeychainStorage_ClientCredentialOperations(t *testing.T) { } func TestIsKeychainAvailable(t *testing.T) { + t.Parallel() // Just test that the function doesn't panic available := IsKeychainAvailable() @@ -243,6 +248,7 @@ func TestIsKeychainAvailable(t *testing.T) { } func TestNewKeychainStorage_Success(t *testing.T) { + t.Parallel() // Skip if keychain is not available if !IsKeychainAvailable() { t.Skip("Keychain not available in test environment") @@ -267,6 +273,7 @@ func TestNewKeychainStorage_Success(t *testing.T) { } func TestKeychainStorage_SaveTokens_MarshalError(t *testing.T) { + t.Parallel() // Skip if keychain is not available if !IsKeychainAvailable() { t.Skip("Keychain not available in test environment") @@ -295,6 +302,7 @@ func TestKeychainStorage_SaveTokens_MarshalError(t *testing.T) { } func TestKeychainStorage_LoadTokens_UnmarshalError(t *testing.T) { + t.Parallel() // Skip if keychain is not available if !IsKeychainAvailable() { t.Skip("Keychain not available in test environment") @@ -332,6 +340,7 @@ func TestKeychainStorage_LoadTokens_UnmarshalError(t *testing.T) { } func TestKeychainStorage_SaveClientCredentials_MarshalError(t *testing.T) { + t.Parallel() // Skip if keychain is not available if !IsKeychainAvailable() { t.Skip("Keychain not available in test environment") @@ -361,6 +370,7 @@ func TestKeychainStorage_SaveClientCredentials_MarshalError(t *testing.T) { } func TestKeychainStorage_LoadClientCredentials_UnmarshalError(t *testing.T) { + t.Parallel() // Skip if keychain is not available if !IsKeychainAvailable() { t.Skip("Keychain not available in test environment") @@ -398,6 +408,7 @@ func TestKeychainStorage_LoadClientCredentials_UnmarshalError(t *testing.T) { } func TestKeychainStorage_MultipleOperations(t *testing.T) { + t.Parallel() // Skip if keychain is not available if !IsKeychainAvailable() { t.Skip("Keychain not available in test environment") diff --git a/pkg/auth/storage/storage_test.go b/pkg/auth/storage/storage_test.go index 8c557c7a..90cd66a8 100644 --- a/pkg/auth/storage/storage_test.go +++ b/pkg/auth/storage/storage_test.go @@ -16,6 +16,7 @@ import ( ) func TestFileStorage_TokenOperations(t *testing.T) { + t.Parallel() // Create temporary directory for testing tempDir := t.TempDir() @@ -99,6 +100,7 @@ func TestFileStorage_TokenOperations(t *testing.T) { } func TestFileStorage_ClientCredentialOperations(t *testing.T) { + t.Parallel() // Create temporary directory for testing tempDir := t.TempDir() @@ -181,6 +183,7 @@ func TestFileStorage_ClientCredentialOperations(t *testing.T) { } func TestFileStorage_GetBackendType(t *testing.T) { + t.Parallel() storage := &FileStorage{baseDir: "/tmp"} if storage.GetBackendType() != BackendFile { @@ -189,6 +192,7 @@ func TestFileStorage_GetBackendType(t *testing.T) { } func TestFileStorage_GetStorageLocation(t *testing.T) { + t.Parallel() baseDir := "/tmp/test" storage := &FileStorage{baseDir: baseDir} @@ -199,6 +203,7 @@ func TestFileStorage_GetStorageLocation(t *testing.T) { } func TestSanitizeSite(t *testing.T) { + t.Parallel() tests := []struct { input string expected string @@ -232,6 +237,7 @@ func TestSanitizeSite(t *testing.T) { } func TestNewFileStorage(t *testing.T) { + t.Parallel() storage, err := NewFileStorage() if err != nil { t.Fatalf("NewFileStorage failed: %v", err) @@ -262,6 +268,7 @@ func TestNewFileStorage(t *testing.T) { } func TestFileStorage_SaveTokens_InvalidJSON(t *testing.T) { + t.Parallel() // This test verifies error handling, though JSON marshal rarely fails // for standard types tempDir := t.TempDir() @@ -282,6 +289,7 @@ func TestFileStorage_SaveTokens_InvalidJSON(t *testing.T) { } func TestFileStorage_LoadTokens_InvalidJSON(t *testing.T) { + t.Parallel() tempDir := t.TempDir() storage := &FileStorage{baseDir: tempDir} @@ -306,6 +314,7 @@ func TestFileStorage_LoadTokens_InvalidJSON(t *testing.T) { } func TestFileStorage_SaveClientCredentials_InvalidJSON(t *testing.T) { + t.Parallel() tempDir := t.TempDir() storage := &FileStorage{baseDir: tempDir} @@ -325,6 +334,7 @@ func TestFileStorage_SaveClientCredentials_InvalidJSON(t *testing.T) { } func TestFileStorage_LoadClientCredentials_InvalidJSON(t *testing.T) { + t.Parallel() tempDir := t.TempDir() storage := &FileStorage{baseDir: tempDir} @@ -349,6 +359,7 @@ func TestFileStorage_LoadClientCredentials_InvalidJSON(t *testing.T) { } func TestFileStorage_DeleteTokens_AlreadyDeleted(t *testing.T) { + t.Parallel() tempDir := t.TempDir() storage := &FileStorage{baseDir: tempDir} @@ -368,6 +379,7 @@ func TestFileStorage_DeleteTokens_AlreadyDeleted(t *testing.T) { } func TestFileStorage_DeleteClientCredentials_AlreadyDeleted(t *testing.T) { + t.Parallel() tempDir := t.TempDir() storage := &FileStorage{baseDir: tempDir} @@ -387,6 +399,7 @@ func TestFileStorage_DeleteClientCredentials_AlreadyDeleted(t *testing.T) { } func TestFileStorage_MultipleOperations(t *testing.T) { + t.Parallel() tempDir := t.TempDir() storage := &FileStorage{baseDir: tempDir} diff --git a/pkg/auth/types/types_test.go b/pkg/auth/types/types_test.go index ed5de8b3..c1bd4db2 100644 --- a/pkg/auth/types/types_test.go +++ b/pkg/auth/types/types_test.go @@ -11,46 +11,48 @@ import ( ) func TestTokenSet_IsExpired(t *testing.T) { + t.Parallel() tests := []struct { - name string - issuedAt int64 + name string + issuedAt int64 expiresIn int64 - expected bool + expected bool }{ { - name: "token expired", - issuedAt: time.Now().Add(-2 * time.Hour).Unix(), + name: "token expired", + issuedAt: time.Now().Add(-2 * time.Hour).Unix(), expiresIn: 3600, // 1 hour - expected: true, + expected: true, }, { - name: "token valid - just issued", - issuedAt: time.Now().Unix(), + name: "token valid - just issued", + issuedAt: time.Now().Unix(), expiresIn: 3600, // 1 hour - expected: false, + expected: false, }, { - name: "token expiring soon (within 5 min buffer)", - issuedAt: time.Now().Add(-56 * time.Minute).Unix(), // 56 minutes ago - expiresIn: 3600, // expires in 4 minutes, within 5 min buffer - expected: true, + name: "token expiring soon (within 5 min buffer)", + issuedAt: time.Now().Add(-56 * time.Minute).Unix(), // 56 minutes ago + expiresIn: 3600, // expires in 4 minutes, within 5 min buffer + expected: true, }, { - name: "token valid - 10 minutes left", - issuedAt: time.Now().Add(-50 * time.Minute).Unix(), + name: "token valid - 10 minutes left", + issuedAt: time.Now().Add(-50 * time.Minute).Unix(), expiresIn: 3600, // 10 minutes left - expected: false, + expected: false, }, { - name: "token valid - long expiry", - issuedAt: time.Now().Unix(), + name: "token valid - long expiry", + issuedAt: time.Now().Unix(), expiresIn: 86400, // 24 hours - expected: false, + expected: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + t.Parallel() token := &TokenSet{ IssuedAt: tt.issuedAt, ExpiresIn: tt.expiresIn, @@ -67,6 +69,7 @@ func TestTokenSet_IsExpired(t *testing.T) { } func TestOAuthError_String(t *testing.T) { + t.Parallel() tests := []struct { name string err OAuthError @@ -107,6 +110,7 @@ func TestOAuthError_String(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + t.Parallel() result := tt.err.String() if result != tt.expected { t.Errorf("String() = %q, want %q", result, tt.expected) @@ -116,6 +120,7 @@ func TestOAuthError_String(t *testing.T) { } func TestDefaultScopes(t *testing.T) { + t.Parallel() scopes := DefaultScopes() // Verify we have all expected scopes (matching PR #84) @@ -207,6 +212,7 @@ func TestDefaultScopes(t *testing.T) { } func TestTokenSet_JSONTags(t *testing.T) { + t.Parallel() // Verify JSON tags are camelCase (matching PR #84) token := TokenSet{ AccessToken: "test-access", @@ -229,6 +235,7 @@ func TestTokenSet_JSONTags(t *testing.T) { } func TestClientCredentials_Structure(t *testing.T) { + t.Parallel() // Verify ClientCredentials structure matches PR #84 creds := ClientCredentials{ ClientID: "test-id", diff --git a/pkg/client/client.go b/pkg/client/client.go index 20e8f5b1..68f91342 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -156,4 +156,3 @@ func (c *Client) RawRequest(method, path string, body io.Reader) (*http.Response return resp, nil } - diff --git a/pkg/client/client_test.go b/pkg/client/client_test.go index 6485bbe2..fb70ba44 100644 --- a/pkg/client/client_test.go +++ b/pkg/client/client_test.go @@ -21,6 +21,7 @@ import ( ) func TestNew_WithAPIKeys(t *testing.T) { + t.Parallel() cfg := &config.Config{ APIKey: "test-api-key", AppKey: "test-app-key", @@ -64,6 +65,7 @@ func TestNew_WithAPIKeys(t *testing.T) { } func TestNew_NoAuthentication(t *testing.T) { + t.Parallel() cfg := &config.Config{ APIKey: "", AppKey: "", @@ -81,6 +83,7 @@ func TestNew_NoAuthentication(t *testing.T) { } func TestNew_MissingAPIKey(t *testing.T) { + t.Parallel() cfg := &config.Config{ APIKey: "", AppKey: "test-app-key", @@ -98,6 +101,7 @@ func TestNew_MissingAPIKey(t *testing.T) { } func TestNew_MissingAppKey(t *testing.T) { + t.Parallel() cfg := &config.Config{ APIKey: "test-api-key", AppKey: "", @@ -115,6 +119,7 @@ func TestNew_MissingAppKey(t *testing.T) { } func TestNew_DifferentSites(t *testing.T) { + t.Parallel() tests := []struct { name string site string @@ -129,6 +134,7 @@ func TestNew_DifferentSites(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + t.Parallel() cfg := &config.Config{ APIKey: "test-api-key", AppKey: "test-app-key", @@ -152,6 +158,7 @@ func TestNew_DifferentSites(t *testing.T) { } func TestClient_Context(t *testing.T) { + t.Parallel() cfg := &config.Config{ APIKey: "test-api-key", AppKey: "test-app-key", @@ -180,6 +187,7 @@ func TestClient_Context(t *testing.T) { } func TestClient_V1(t *testing.T) { + t.Parallel() cfg := &config.Config{ APIKey: "test-api-key", AppKey: "test-app-key", @@ -203,6 +211,7 @@ func TestClient_V1(t *testing.T) { } func TestClient_V2(t *testing.T) { + t.Parallel() cfg := &config.Config{ APIKey: "test-api-key", AppKey: "test-app-key", @@ -226,6 +235,7 @@ func TestClient_V2(t *testing.T) { } func TestClient_API(t *testing.T) { + t.Parallel() cfg := &config.Config{ APIKey: "test-api-key", AppKey: "test-app-key", @@ -254,6 +264,7 @@ func TestClient_API(t *testing.T) { } func TestClient_Config(t *testing.T) { + t.Parallel() cfg := &config.Config{ APIKey: "test-api-key", AppKey: "test-app-key", @@ -280,6 +291,7 @@ func TestClient_Config(t *testing.T) { } func TestRawRequest_APIKeyAuth(t *testing.T) { + t.Parallel() var gotHeaders http.Header server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { gotHeaders = r.Header @@ -372,6 +384,7 @@ func TestRawRequest_APIKeyAuth(t *testing.T) { } func TestRawRequest_OAuth2Auth(t *testing.T) { + t.Parallel() var gotHeaders http.Header server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { gotHeaders = r.Header @@ -409,6 +422,7 @@ func TestRawRequest_OAuth2Auth(t *testing.T) { } func TestRawRequest_WithBody(t *testing.T) { + t.Parallel() var gotBody string server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { bodyBytes, _ := io.ReadAll(r.Body) @@ -442,6 +456,7 @@ func TestRawRequest_WithBody(t *testing.T) { } func TestRawRequest_NilBody(t *testing.T) { + t.Parallel() server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) _, _ = w.Write([]byte(`{"data":[]}`)) @@ -466,6 +481,7 @@ func TestRawRequest_NilBody(t *testing.T) { } func TestClient_APIConfiguration(t *testing.T) { + t.Parallel() cfg := &config.Config{ APIKey: "test-api-key", AppKey: "test-app-key", @@ -494,6 +510,7 @@ func TestClient_APIConfiguration(t *testing.T) { } func TestGetUserAgent(t *testing.T) { + t.Parallel() userAgent := useragent.Get() // Check that it starts with "pup/" @@ -540,6 +557,7 @@ func (c *captureTransport) RoundTrip(req *http.Request) (*http.Response, error) } func TestClient_IntegrationUserAgentInAPIClient(t *testing.T) { + t.Parallel() // Integration test: verify User-Agent is automatically set by API client configuration // This captures actual requests made through the Datadog API client diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index 7f92d31b..0d5cdaf7 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -11,6 +11,7 @@ import ( ) func TestLoad(t *testing.T) { + // NOTE: Not parallel - modifies env vars // Save original env vars origAPIKey := os.Getenv("DD_API_KEY") origAppKey := os.Getenv("DD_APP_KEY") @@ -119,6 +120,7 @@ func TestLoad(t *testing.T) { } func TestConfig_Validate(t *testing.T) { + t.Parallel() tests := []struct { name string config *Config @@ -165,6 +167,7 @@ func TestConfig_Validate(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + t.Parallel() err := tt.config.Validate() if (err != nil) != tt.wantErr { t.Errorf("Validate() error = %v, wantErr %v", err, tt.wantErr) @@ -174,6 +177,7 @@ func TestConfig_Validate(t *testing.T) { } func TestConfig_GetAPIURL(t *testing.T) { + t.Parallel() tests := []struct { name string site string @@ -218,6 +222,7 @@ func TestConfig_GetAPIURL(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + t.Parallel() cfg := &Config{Site: tt.site} got := cfg.GetAPIURL() if got != tt.want { @@ -228,6 +233,7 @@ func TestConfig_GetAPIURL(t *testing.T) { } func TestGetEnvWithDefault(t *testing.T) { + t.Parallel() // Save original origValue := os.Getenv("TEST_ENV_VAR") defer os.Setenv("TEST_ENV_VAR", origValue) @@ -267,6 +273,7 @@ func TestGetEnvWithDefault(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + t.Parallel() if tt.setEnv { os.Setenv(tt.key, tt.setValue) } else { diff --git a/pkg/formatter/formatter_test.go b/pkg/formatter/formatter_test.go index 4f53c232..a8ae2b5f 100644 --- a/pkg/formatter/formatter_test.go +++ b/pkg/formatter/formatter_test.go @@ -12,10 +12,11 @@ import ( ) func TestToJSON(t *testing.T) { + t.Parallel() tests := []struct { - name string - data interface{} - wantError bool + name string + data interface{} + wantError bool wantContains []string }{ { @@ -24,7 +25,7 @@ func TestToJSON(t *testing.T) { "foo": "bar", "baz": 123, }, - wantError: false, + wantError: false, wantContains: []string{`"foo"`, `"bar"`, `"baz"`, `123`}, }, { @@ -36,31 +37,32 @@ func TestToJSON(t *testing.T) { Name: "Alice", Age: 30, }, - wantError: false, + wantError: false, wantContains: []string{`"name"`, `"Alice"`, `"age"`, `30`}, }, { - name: "array", - data: []string{"a", "b", "c"}, - wantError: false, + name: "array", + data: []string{"a", "b", "c"}, + wantError: false, wantContains: []string{`"a"`, `"b"`, `"c"`}, }, { - name: "nil", - data: nil, - wantError: false, + name: "nil", + data: nil, + wantError: false, wantContains: []string{`null`}, }, { - name: "empty map", - data: map[string]interface{}{}, - wantError: false, + name: "empty map", + data: map[string]interface{}{}, + wantError: false, wantContains: []string{`{}`}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + t.Parallel() result, err := ToJSON(tt.data) if tt.wantError { @@ -85,6 +87,7 @@ func TestToJSON(t *testing.T) { } func TestToTable(t *testing.T) { + t.Parallel() tests := []struct { name string data interface{} @@ -287,6 +290,7 @@ func TestToTable(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + t.Parallel() result, err := ToTable(tt.data) if tt.wantError { @@ -311,6 +315,7 @@ func TestToTable(t *testing.T) { } func TestToYAML(t *testing.T) { + t.Parallel() tests := []struct { name string data interface{} @@ -327,8 +332,8 @@ func TestToYAML(t *testing.T) { wantContains: []string{"name:", "test", "value:", "42"}, }, { - name: "slice data", - data: []string{"a", "b", "c"}, + name: "slice data", + data: []string{"a", "b", "c"}, wantError: false, wantContains: []string{"a", "b", "c"}, }, @@ -341,6 +346,7 @@ func TestToYAML(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + t.Parallel() result, err := ToYAML(tt.data) if tt.wantError { @@ -365,6 +371,7 @@ func TestToYAML(t *testing.T) { } func TestFormatOutput(t *testing.T) { + t.Parallel() data := map[string]string{"key": "value"} tests := []struct { @@ -401,6 +408,7 @@ func TestFormatOutput(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + t.Parallel() result, err := FormatOutput(data, tt.format) if tt.wantError { @@ -428,6 +436,7 @@ func TestFormatOutput(t *testing.T) { } func TestFormatError(t *testing.T) { + t.Parallel() tests := []struct { name string err error @@ -447,6 +456,7 @@ func TestFormatError(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + t.Parallel() result := FormatError(tt.err) if result != tt.want { t.Errorf("FormatError() = %q, want %q", result, tt.want) @@ -456,6 +466,7 @@ func TestFormatError(t *testing.T) { } func TestFormatSuccess(t *testing.T) { + t.Parallel() tests := []struct { name string message string @@ -508,6 +519,7 @@ func TestFormatSuccess(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + t.Parallel() result, err := FormatSuccess(tt.message, tt.data) if tt.wantError { @@ -532,6 +544,7 @@ func TestFormatSuccess(t *testing.T) { } func TestOutputFormat_Constants(t *testing.T) { + t.Parallel() // Verify format constants are correctly defined if FormatJSON != "json" { t.Errorf("FormatJSON = %q, want \"json\"", FormatJSON) @@ -545,6 +558,7 @@ func TestOutputFormat_Constants(t *testing.T) { } func TestFormatTableValue(t *testing.T) { + t.Parallel() tests := []struct { name string val interface{} @@ -644,6 +658,7 @@ func TestFormatTableValue(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + t.Parallel() result := formatTableValue(tt.val) if result != tt.want { t.Errorf("formatTableValue(%v) = %q, want %q", tt.val, result, tt.want) @@ -653,6 +668,7 @@ func TestFormatTableValue(t *testing.T) { } func TestToJSON_Error(t *testing.T) { + t.Parallel() // Test with unmarshalable data (channels can't be marshaled to JSON) ch := make(chan int) _, err := ToJSON(ch) @@ -669,6 +685,7 @@ func TestToJSON_Error(t *testing.T) { // by internal yaml library errors which are hard to simulate in a unit test. func TestToTable_EdgeCases(t *testing.T) { + t.Parallel() tests := []struct { name string data interface{} @@ -676,9 +693,9 @@ func TestToTable_EdgeCases(t *testing.T) { wantContains []string }{ { - name: "slice of non-maps", - data: []interface{}{"string1", "string2", "string3"}, - wantError: false, + name: "slice of non-maps", + data: []interface{}{"string1", "string2", "string3"}, + wantError: false, wantContains: []string{"string1", "string2", "string3"}, }, { @@ -731,9 +748,9 @@ func TestToTable_EdgeCases(t *testing.T) { wantContains: []string{"incident-1", "Outage", "user-1,user-2,user-3"}, }, { - name: "unsupported type (fails at marshal)", - data: func() {}, - wantError: true, // Functions can't be marshaled to JSON + name: "unsupported type (fails at marshal)", + data: func() {}, + wantError: true, // Functions can't be marshaled to JSON wantContains: []string{}, }, { @@ -748,6 +765,7 @@ func TestToTable_EdgeCases(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + t.Parallel() result, err := ToTable(tt.data) if tt.wantError { @@ -772,6 +790,7 @@ func TestToTable_EdgeCases(t *testing.T) { } func TestFlattenJSONAPIObject(t *testing.T) { + t.Parallel() tests := []struct { name string obj map[string]interface{} @@ -898,6 +917,7 @@ func TestFlattenJSONAPIObject(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + t.Parallel() result := flattenJSONAPIObject(tt.obj) // Check all expected keys diff --git a/pkg/useragent/useragent_test.go b/pkg/useragent/useragent_test.go index 1becadfc..04042cc3 100644 --- a/pkg/useragent/useragent_test.go +++ b/pkg/useragent/useragent_test.go @@ -15,6 +15,7 @@ import ( ) func TestGet_NoAgent(t *testing.T) { + // NOTE: Not parallel - modifies env vars // Clear all agent environment variables os.Unsetenv("CLAUDECODE") os.Unsetenv("CLAUDE_CODE") @@ -35,6 +36,7 @@ func TestGet_NoAgent(t *testing.T) { } func TestGet_WithClaudeCode(t *testing.T) { + // NOTE: Not parallel - modifies env vars tests := []struct { name string envVar string diff --git a/pkg/util/time.go b/pkg/util/time.go index 744522f0..7b2b9d14 100644 --- a/pkg/util/time.go +++ b/pkg/util/time.go @@ -24,6 +24,7 @@ import ( // - Long: "1week", "1weeks" // - With spaces: "5 minutes", "2 hours" // - With minus prefix: "-5m", "-2h" (treated same as "5m", "2h") +// // - ISO date strings (e.g., "2024-01-01T00:00:00Z") func ParseTimeParam(timeStr string) (time.Time, error) { // Handle "now" (case-insensitive) diff --git a/pkg/util/time_test.go b/pkg/util/time_test.go index 37ef2b01..d9a1dd79 100644 --- a/pkg/util/time_test.go +++ b/pkg/util/time_test.go @@ -11,6 +11,7 @@ import ( ) func TestParseTimeParam(t *testing.T) { + t.Parallel() now := time.Now() tests := []struct {