diff --git a/README.md b/README.md index 7f591fe..17abecf 100644 --- a/README.md +++ b/README.md @@ -154,7 +154,7 @@ CODEX_API_PORT=8001 gotestsum --packages="./communities" -f standard-verbose --r or to run all integration tests: ```bash -CODEX_API_PORT=8001 gotestsum --packages="./communities" -f standard-verbose --rerun-fails -- -tags=integration -v -count 1 +CODEX_API_PORT=8001 gotestsum --packages="./communities" -f standard-verbose --rerun-fails -- -tags=integration -v -count 1 -run Integration ``` I prefer to be more selective when running integration tests. diff --git a/communities/codex_client_integration_test.go b/communities/codex_client_integration_test.go index ef42c32..c3bc650 100644 --- a/communities/codex_client_integration_test.go +++ b/communities/codex_client_integration_test.go @@ -12,124 +12,111 @@ import ( "testing" "time" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + "go-codex-client/communities" ) -// This test exercises real network calls against a running Codex node. -// It is disabled by default via the "integration" build tag. -// Run with: -// -// go test -v -tags=integration ./communities -run Integration -// +// CodexClientIntegrationTestSuite demonstrates testify's suite functionality for CodexClient integration tests +// These tests exercise real network calls against a running Codex node. // Required env vars (with defaults): -// -// CODEX_HOST (default: localhost) -// CODEX_API_PORT (default: 8080) -// CODEX_TIMEOUT_MS (optional; default: 60000) -func TestIntegration_UploadAndDownload(t *testing.T) { - host := getenv("CODEX_HOST", "localhost") - port := getenv("CODEX_API_PORT", "8080") - client := communities.NewCodexClient(host, port) +// - CODEX_HOST (default: localhost) +// - CODEX_API_PORT (default: 8080) +// - CODEX_TIMEOUT_MS (optional; default: 60000) +type CodexClientIntegrationTestSuite struct { + suite.Suite + client *communities.CodexClient + host string + port string +} + +// SetupSuite runs once before all tests in the suite +func (suite *CodexClientIntegrationTestSuite) SetupSuite() { + suite.host = getenv("CODEX_HOST", "localhost") + suite.port = getenv("CODEX_API_PORT", "8080") + suite.client = communities.NewCodexClient(suite.host, suite.port) // Optional request timeout override if ms := os.Getenv("CODEX_TIMEOUT_MS"); ms != "" { if d, err := time.ParseDuration(ms + "ms"); err == nil { - client.SetRequestTimeout(d) + suite.client.SetRequestTimeout(d) } } +} +// TestCodexClientIntegrationTestSuite runs the integration test suite +func TestCodexClientIntegrationTestSuite(t *testing.T) { + suite.Run(t, new(CodexClientIntegrationTestSuite)) +} + +func (suite *CodexClientIntegrationTestSuite) TestIntegration_UploadAndDownload() { // Generate random payload to ensure proper round-trip verification payload := make([]byte, 1024) - if _, err := rand.Read(payload); err != nil { - t.Fatalf("failed to generate random payload: %v", err) - } - t.Logf("Generated payload (first 32 bytes hex): %s", hex.EncodeToString(payload[:32])) + _, err := rand.Read(payload) + require.NoError(suite.T(), err, "failed to generate random payload") + suite.T().Logf("Generated payload (first 32 bytes hex): %s", hex.EncodeToString(payload[:32])) - cid, err := client.Upload(bytes.NewReader(payload), "it.bin") - if err != nil { - t.Fatalf("upload failed: %v", err) - } - t.Logf("Upload successful, CID: %s", cid) + cid, err := suite.client.Upload(bytes.NewReader(payload), "it.bin") + require.NoError(suite.T(), err, "upload failed") + suite.T().Logf("Upload successful, CID: %s", cid) // Clean up after test defer func() { - if err := client.RemoveCid(cid); err != nil { - t.Logf("Warning: Failed to remove CID %s: %v", cid, err) + if err := suite.client.RemoveCid(cid); err != nil { + suite.T().Logf("Warning: Failed to remove CID %s: %v", cid, err) } }() // Verify existence via HasCid - exists, err := client.HasCid(cid) - if err != nil { - t.Fatalf("HasCid failed: %v", err) - } - if !exists { - t.Fatalf("HasCid returned false for uploaded CID %s", cid) - } - t.Logf("HasCid confirmed existence of CID: %s", cid) + exists, err := suite.client.HasCid(cid) + require.NoError(suite.T(), err, "HasCid failed") + assert.True(suite.T(), exists, "HasCid returned false for uploaded CID %s", cid) + suite.T().Logf("HasCid confirmed existence of CID: %s", cid) // Download via network stream with a context timeout to avoid hanging ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() var buf bytes.Buffer - if err := client.DownloadWithContext(ctx, cid, &buf); err != nil { - t.Fatalf("download failed: %v", err) - } - if got := buf.Bytes(); !bytes.Equal(got, payload) { - t.Fatalf("payload mismatch: got %q want %q", string(got), string(payload)) - } + err = suite.client.DownloadWithContext(ctx, cid, &buf) + require.NoError(suite.T(), err, "download failed") + assert.Equal(suite.T(), payload, buf.Bytes(), "payload mismatch") } -func TestIntegration_CheckNonExistingCID(t *testing.T) { - host := getenv("CODEX_HOST", "localhost") - port := getenv("CODEX_API_PORT", "8080") - client := communities.NewCodexClient(host, port) - +func (suite *CodexClientIntegrationTestSuite) TestIntegration_CheckNonExistingCID() { // Generate random payload to ensure proper round-trip verification payload := make([]byte, 1024) - if _, err := rand.Read(payload); err != nil { - t.Fatalf("failed to generate random payload: %v", err) - } - t.Logf("Generated payload (first 32 bytes hex): %s", hex.EncodeToString(payload[:32])) + _, err := rand.Read(payload) + require.NoError(suite.T(), err, "failed to generate random payload") + suite.T().Logf("Generated payload (first 32 bytes hex): %s", hex.EncodeToString(payload[:32])) - cid, err := client.Upload(bytes.NewReader(payload), "it.bin") - if err != nil { - t.Fatalf("upload failed: %v", err) - } - t.Logf("Upload successful, CID: %s", cid) + cid, err := suite.client.Upload(bytes.NewReader(payload), "it.bin") + require.NoError(suite.T(), err, "upload failed") + suite.T().Logf("Upload successful, CID: %s", cid) // Verify existence via HasCid - exists, err := client.HasCid(cid) - if err != nil { - t.Fatalf("HasCid failed: %v", err) - } - if !exists { - t.Fatalf("HasCid returned false for uploaded CID %s", cid) - } - t.Logf("HasCid confirmed existence of CID: %s", cid) + exists, err := suite.client.HasCid(cid) + require.NoError(suite.T(), err, "HasCid failed") + assert.True(suite.T(), exists, "HasCid returned false for uploaded CID %s", cid) + suite.T().Logf("HasCid confirmed existence of CID: %s", cid) // Remove CID from Codex - if err := client.RemoveCid(cid); err != nil { - t.Fatalf("RemoveCid failed: %v", err) - } - t.Logf("RemoveCid confirmed deletion of CID: %s", cid) + err = suite.client.RemoveCid(cid) + require.NoError(suite.T(), err, "RemoveCid failed") + suite.T().Logf("RemoveCid confirmed deletion of CID: %s", cid) - exists, err = client.HasCid(cid) - if err != nil { - t.Fatalf("HasCid failed: %v", err) - } - if exists { - t.Fatalf("HasCid returned true for removed CID %s", cid) - } - t.Logf("HasCid confirmed CID is no longer present: %s", cid) + exists, err = suite.client.HasCid(cid) + require.NoError(suite.T(), err, "HasCid failed after removal") + assert.False(suite.T(), exists, "HasCid returned true for removed CID %s", cid) + suite.T().Logf("HasCid confirmed CID is no longer present: %s", cid) } -func TestIntegration_TriggerDownload(t *testing.T) { - host := getenv("CODEX_HOST", "localhost") - port := getenv("CODEX_API_PORT", "8001") // Use port 8001 as specified by user - client := communities.NewCodexClient(host, port) - +func (suite *CodexClientIntegrationTestSuite) TestIntegration_TriggerDownload() { + // Use port 8001 for this test as specified + client := communities.NewCodexClient(suite.host, "8001") + // Optional request timeout override if ms := os.Getenv("CODEX_TIMEOUT_MS"); ms != "" { if d, err := time.ParseDuration(ms + "ms"); err == nil { @@ -139,31 +126,26 @@ func TestIntegration_TriggerDownload(t *testing.T) { // Generate random payload to ensure proper round-trip verification payload := make([]byte, 1024) - if _, err := rand.Read(payload); err != nil { - t.Fatalf("failed to generate random payload: %v", err) - } - t.Logf("Generated payload (first 32 bytes hex): %s", hex.EncodeToString(payload[:32])) + _, err := rand.Read(payload) + require.NoError(suite.T(), err, "failed to generate random payload") + suite.T().Logf("Generated payload (first 32 bytes hex): %s", hex.EncodeToString(payload[:32])) // Upload the data cid, err := client.Upload(bytes.NewReader(payload), "local-download-test.bin") - if err != nil { - t.Fatalf("upload failed: %v", err) - } - t.Logf("Upload successful, CID: %s", cid) + require.NoError(suite.T(), err, "upload failed") + suite.T().Logf("Upload successful, CID: %s", cid) // Clean up after test defer func() { if err := client.RemoveCid(cid); err != nil { - t.Logf("Warning: Failed to remove CID %s: %v", cid, err) + suite.T().Logf("Warning: Failed to remove CID %s: %v", cid, err) } }() // Trigger async download manifest, err := client.TriggerDownload(cid) - if err != nil { - t.Fatalf("TriggerDownload failed: %v", err) - } - t.Logf("Async download triggered, manifest CID: %s", manifest.CID) + require.NoError(suite.T(), err, "TriggerDownload failed") + suite.T().Logf("Async download triggered, manifest CID: %s", manifest.CID) // Poll HasCid for up to 10 seconds using goroutine and channel downloadComplete := make(chan bool, 1) @@ -173,15 +155,15 @@ func TestIntegration_TriggerDownload(t *testing.T) { for range ticker.C { hasCid, err := client.HasCid(cid) if err != nil { - t.Logf("HasCid check failed: %v", err) + suite.T().Logf("HasCid check failed: %v", err) continue } if hasCid { - t.Logf("CID is now available locally") + suite.T().Logf("CID is now available locally") downloadComplete <- true return } else { - t.Logf("CID not yet available locally, continuing to poll...") + suite.T().Logf("CID not yet available locally, continuing to poll...") } } }() @@ -191,7 +173,7 @@ func TestIntegration_TriggerDownload(t *testing.T) { case <-downloadComplete: // Download completed successfully case <-time.After(10 * time.Second): - t.Fatalf("Timeout waiting for CID to be available locally after 10 seconds") + suite.T().Fatalf("Timeout waiting for CID to be available locally after 10 seconds") } // Now download the actual content from local storage and verify it matches @@ -199,110 +181,73 @@ func TestIntegration_TriggerDownload(t *testing.T) { defer cancel() var downloadBuf bytes.Buffer - if err := client.LocalDownloadWithContext(ctx, cid, &downloadBuf); err != nil { - t.Fatalf("LocalDownload after trigger download failed: %v", err) - } + err = client.LocalDownloadWithContext(ctx, cid, &downloadBuf) + require.NoError(suite.T(), err, "LocalDownload after trigger download failed") downloadedData := downloadBuf.Bytes() - t.Logf("Downloaded data (first 32 bytes hex): %s", hex.EncodeToString(downloadedData[:32])) + suite.T().Logf("Downloaded data (first 32 bytes hex): %s", hex.EncodeToString(downloadedData[:32])) // Verify the data matches - if !bytes.Equal(payload, downloadedData) { - t.Errorf("Downloaded data does not match uploaded data") - t.Errorf("Expected length: %d, got: %d", len(payload), len(downloadedData)) - } + assert.Equal(suite.T(), payload, downloadedData, "Downloaded data does not match uploaded data") } -func TestIntegration_FetchManifest(t *testing.T) { - host := getenv("CODEX_HOST", "localhost") - port := getenv("CODEX_API_PORT", "8080") - client := communities.NewCodexClient(host, port) - - // Optional request timeout override - if ms := os.Getenv("CODEX_TIMEOUT_MS"); ms != "" { - if d, err := time.ParseDuration(ms + "ms"); err == nil { - client.SetRequestTimeout(d) - } - } - +func (suite *CodexClientIntegrationTestSuite) TestIntegration_FetchManifest() { // Generate random payload to ensure proper round-trip verification payload := make([]byte, 1024) - if _, err := rand.Read(payload); err != nil { - t.Fatalf("failed to generate random payload: %v", err) - } - t.Logf("Generated payload (first 32 bytes hex): %s", hex.EncodeToString(payload[:32])) + _, err := rand.Read(payload) + require.NoError(suite.T(), err, "failed to generate random payload") + suite.T().Logf("Generated payload (first 32 bytes hex): %s", hex.EncodeToString(payload[:32])) - cid, err := client.Upload(bytes.NewReader(payload), "fetch-manifest-test.bin") - if err != nil { - t.Fatalf("upload failed: %v", err) - } - t.Logf("Upload successful, CID: %s", cid) + cid, err := suite.client.Upload(bytes.NewReader(payload), "fetch-manifest-test.bin") + require.NoError(suite.T(), err, "upload failed") + suite.T().Logf("Upload successful, CID: %s", cid) // Clean up after test defer func() { - if err := client.RemoveCid(cid); err != nil { - t.Logf("Warning: Failed to remove CID %s: %v", cid, err) + if err := suite.client.RemoveCid(cid); err != nil { + suite.T().Logf("Warning: Failed to remove CID %s: %v", cid, err) } }() // Verify existence via HasCid first - exists, err := client.HasCid(cid) - if err != nil { - t.Fatalf("HasCid failed: %v", err) - } - if !exists { - t.Fatalf("HasCid returned false for uploaded CID %s", cid) - } - t.Logf("HasCid confirmed existence of CID: %s", cid) + exists, err := suite.client.HasCid(cid) + require.NoError(suite.T(), err, "HasCid failed") + assert.True(suite.T(), exists, "HasCid returned false for uploaded CID %s", cid) + suite.T().Logf("HasCid confirmed existence of CID: %s", cid) // Fetch manifest with context timeout ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() - manifest, err := client.FetchManifestWithContext(ctx, cid) - if err != nil { - t.Fatalf("FetchManifestWithContext failed: %v", err) - } - t.Logf("FetchManifest successful, manifest CID: %s", manifest.CID) + manifest, err := suite.client.FetchManifestWithContext(ctx, cid) + require.NoError(suite.T(), err, "FetchManifestWithContext failed") + suite.T().Logf("FetchManifest successful, manifest CID: %s", manifest.CID) // Verify manifest properties - if manifest.CID != cid { - t.Errorf("Manifest CID mismatch: expected %s, got %s", cid, manifest.CID) - } + assert.Equal(suite.T(), cid, manifest.CID, "Manifest CID mismatch") // Verify manifest has expected fields - if manifest.Manifest.TreeCid == "" { - t.Error("Expected TreeCid to be non-empty") - } - t.Logf("Manifest TreeCid: %s", manifest.Manifest.TreeCid) + assert.NotEmpty(suite.T(), manifest.Manifest.TreeCid, "Expected TreeCid to be non-empty") + suite.T().Logf("Manifest TreeCid: %s", manifest.Manifest.TreeCid) - if manifest.Manifest.DatasetSize <= 0 { - t.Errorf("Expected DatasetSize > 0, got %d", manifest.Manifest.DatasetSize) - } - t.Logf("Manifest DatasetSize: %d", manifest.Manifest.DatasetSize) + assert.Greater(suite.T(), manifest.Manifest.DatasetSize, int64(0), "Expected DatasetSize > 0") + suite.T().Logf("Manifest DatasetSize: %d", manifest.Manifest.DatasetSize) - if manifest.Manifest.BlockSize <= 0 { - t.Errorf("Expected BlockSize > 0, got %d", manifest.Manifest.BlockSize) - } - t.Logf("Manifest BlockSize: %d", manifest.Manifest.BlockSize) + assert.Greater(suite.T(), manifest.Manifest.BlockSize, 0, "Expected BlockSize > 0") + suite.T().Logf("Manifest BlockSize: %d", manifest.Manifest.BlockSize) - if manifest.Manifest.Filename != "fetch-manifest-test.bin" { - t.Errorf("Expected Filename 'fetch-manifest-test.bin', got '%s'", manifest.Manifest.Filename) - } - t.Logf("Manifest Filename: %s", manifest.Manifest.Filename) + assert.Equal(suite.T(), "fetch-manifest-test.bin", manifest.Manifest.Filename, "Filename mismatch") + suite.T().Logf("Manifest Filename: %s", manifest.Manifest.Filename) // Log manifest details for verification - t.Logf("Manifest Protected: %v", manifest.Manifest.Protected) - t.Logf("Manifest Mimetype: %s", manifest.Manifest.Mimetype) + suite.T().Logf("Manifest Protected: %v", manifest.Manifest.Protected) + suite.T().Logf("Manifest Mimetype: %s", manifest.Manifest.Mimetype) // Test fetching manifest for non-existent CID (should fail gracefully) nonExistentCID := "zDvZRwzmNonExistentCID123456789" - _, err = client.FetchManifestWithContext(ctx, nonExistentCID) - if err == nil { - t.Error("Expected error when fetching manifest for non-existent CID, got nil") - } else { - t.Logf("Expected error for non-existent CID: %v", err) - } + _, err = suite.client.FetchManifestWithContext(ctx, nonExistentCID) + assert.Error(suite.T(), err, "Expected error when fetching manifest for non-existent CID") + suite.T().Logf("Expected error for non-existent CID: %v", err) } func getenv(k, def string) string { diff --git a/communities/codex_client_test.go b/communities/codex_client_test.go index f7369a9..10f1f90 100644 --- a/communities/codex_client_test.go +++ b/communities/codex_client_test.go @@ -8,16 +8,44 @@ import ( "io" "net/http" "net/http/httptest" - "strings" "testing" "time" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + "go-codex-client/communities" ) -func TestUpload_Success(t *testing.T) { +// CodexClientTestSuite demonstrates testify's suite functionality for CodexClient tests +type CodexClientTestSuite struct { + suite.Suite + client *communities.CodexClient + server *httptest.Server +} + +// SetupTest runs before each test method +func (suite *CodexClientTestSuite) SetupTest() { + suite.client = communities.NewCodexClient("localhost", "8080") +} + +// TearDownTest runs after each test method +func (suite *CodexClientTestSuite) TearDownTest() { + if suite.server != nil { + suite.server.Close() + suite.server = nil + } +} + +// TestCodexClientTestSuite runs the test suite +func TestCodexClientTestSuite(t *testing.T) { + suite.Run(t, new(CodexClientTestSuite)) +} + +func (suite *CodexClientTestSuite) TestUpload_Success() { // Arrange a fake Codex server that validates headers and returns a CID - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + suite.server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { w.WriteHeader(http.StatusMethodNotAllowed) return @@ -47,29 +75,23 @@ func TestUpload_Success(t *testing.T) { // we add a newline to simulate real response _, _ = w.Write([]byte("zDvZRwzmTestCID123\n")) })) - defer server.Close() - client := communities.NewCodexClient("localhost", "8080") - client.BaseURL = server.URL + suite.client.BaseURL = suite.server.URL // Act - cid, err := client.Upload(bytes.NewReader([]byte("payload")), "hello.txt") + cid, err := suite.client.Upload(bytes.NewReader([]byte("payload")), "hello.txt") // Assert - if err != nil { - t.Fatalf("unexpected error: %v", err) - } + require.NoError(suite.T(), err) // Codex uses CIDv1 with base58btc encoding (prefix: zDv) - if cid != "zDvZRwzmTestCID123" { - t.Fatalf("unexpected cid: %q", cid) - } + assert.Equal(suite.T(), "zDvZRwzmTestCID123", cid) } -func TestDownload_Success(t *testing.T) { +func (suite *CodexClientTestSuite) TestDownload_Success() { const wantCID = "zDvZRwzm" const payload = "hello from codex" - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + suite.server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { w.WriteHeader(http.StatusMethodNotAllowed) return @@ -82,24 +104,19 @@ func TestDownload_Success(t *testing.T) { w.WriteHeader(http.StatusOK) _, _ = w.Write([]byte(payload)) })) - defer server.Close() - client := communities.NewCodexClient("localhost", "8080") - client.BaseURL = server.URL + suite.client.BaseURL = suite.server.URL var buf bytes.Buffer - if err := client.Download(wantCID, &buf); err != nil { - t.Fatalf("unexpected error: %v", err) - } - if got := buf.String(); got != payload { - t.Fatalf("unexpected payload: %q", got) - } + err := suite.client.Download(wantCID, &buf) + require.NoError(suite.T(), err) + assert.Equal(suite.T(), payload, buf.String()) } -func TestDownloadWithContext_Cancel(t *testing.T) { +func (suite *CodexClientTestSuite) TestDownloadWithContext_Cancel() { const cid = "zDvZRwzm" - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + suite.server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/api/codex/v1/data/"+cid+"/network/stream" { w.WriteHeader(http.StatusNotFound) return @@ -124,29 +141,25 @@ func TestDownloadWithContext_Cancel(t *testing.T) { time.Sleep(10 * time.Millisecond) } })) - defer server.Close() - client := communities.NewCodexClient("localhost", "8080") - client.BaseURL = server.URL + suite.client.BaseURL = suite.server.URL ctx, cancel := context.WithTimeout(context.Background(), 30*time.Millisecond) defer cancel() - err := client.DownloadWithContext(ctx, cid, io.Discard) - if err == nil { - t.Fatalf("expected cancellation error, got nil") - } + err := suite.client.DownloadWithContext(ctx, cid, io.Discard) + require.Error(suite.T(), err) // Accept either canceled or deadline exceeded depending on timing if !errors.Is(err, context.Canceled) && !errors.Is(err, context.DeadlineExceeded) { // net/http may wrap the context error; check error string as a fallback es := err.Error() if !(es == context.Canceled.Error() || es == context.DeadlineExceeded.Error()) { - t.Fatalf("expected context cancellation, got: %v", err) + suite.T().Fatalf("expected context cancellation, got: %v", err) } } } -func TestHasCid_Success(t *testing.T) { +func (suite *CodexClientTestSuite) TestHasCid_Success() { tests := []struct { name string cid string @@ -158,8 +171,8 @@ func TestHasCid_Success(t *testing.T) { } for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + suite.Run(tt.name, func() { + suite.server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/api/codex/v1/data/"+tt.cid+"/exists" { w.WriteHeader(http.StatusNotFound) return @@ -169,71 +182,52 @@ func TestHasCid_Success(t *testing.T) { // Return JSON: {"": } fmt.Fprintf(w, `{"%s": %t}`, tt.cid, tt.hasIt) })) - defer server.Close() - client := communities.NewCodexClient("localhost", "8080") - client.BaseURL = server.URL + suite.client.BaseURL = suite.server.URL - got, err := client.HasCid(tt.cid) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if got != tt.wantBool { - t.Fatalf("HasCid(%q) = %v, want %v", tt.cid, got, tt.wantBool) - } + got, err := suite.client.HasCid(tt.cid) + require.NoError(suite.T(), err) + assert.Equal(suite.T(), tt.wantBool, got, "HasCid(%q) = %v, want %v", tt.cid, got, tt.wantBool) }) } } -func TestHasCid_RequestError(t *testing.T) { +func (suite *CodexClientTestSuite) TestHasCid_RequestError() { // Create a server and immediately close it to trigger connection error - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})) - server.Close() // Close immediately so connection fails + suite.server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})) + suite.server.Close() // Close immediately so connection fails - client := communities.NewCodexClient("localhost", "8080") - client.BaseURL = server.URL // Use the closed server's URL + suite.client.BaseURL = suite.server.URL // Use the closed server's URL - got, err := client.HasCid("zDvZRwzmTestCID") - if err == nil { - t.Fatal("expected error, got nil") - } - if got != false { - t.Fatalf("expected false on error, got %v", got) - } + got, err := suite.client.HasCid("zDvZRwzmTestCID") + require.Error(suite.T(), err) + assert.False(suite.T(), got, "expected false on error") } -func TestHasCid_CidMismatch(t *testing.T) { +func (suite *CodexClientTestSuite) TestHasCid_CidMismatch() { const requestCid = "zDvZRwzmRequestCID" const responseCid = "zDvZRwzmDifferentCID" - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + suite.server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) // Return a different CID in the response fmt.Fprintf(w, `{"%s": true}`, responseCid) })) - defer server.Close() - client := communities.NewCodexClient("localhost", "8080") - client.BaseURL = server.URL + suite.client.BaseURL = suite.server.URL - got, err := client.HasCid(requestCid) - if err == nil { - t.Fatal("expected error for CID mismatch, got nil") - } - if got != false { - t.Fatalf("expected false on CID mismatch, got %v", got) - } + got, err := suite.client.HasCid(requestCid) + require.Error(suite.T(), err, "expected error for CID mismatch") + assert.False(suite.T(), got, "expected false on CID mismatch") // Check error message mentions the missing/mismatched CID - if !strings.Contains(err.Error(), requestCid) { - t.Fatalf("error should mention request CID %q, got: %v", requestCid, err) - } + assert.Contains(suite.T(), err.Error(), requestCid, "error should mention request CID") } -func TestRemoveCid_Success(t *testing.T) { +func (suite *CodexClientTestSuite) TestRemoveCid_Success() { const testCid = "zDvZRwzmTestCID" - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + suite.server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodDelete { w.WriteHeader(http.StatusMethodNotAllowed) return @@ -245,40 +239,30 @@ func TestRemoveCid_Success(t *testing.T) { // DELETE should return 204 No Content w.WriteHeader(http.StatusNoContent) })) - defer server.Close() - client := communities.NewCodexClient("localhost", "8080") - client.BaseURL = server.URL + suite.client.BaseURL = suite.server.URL - err := client.RemoveCid(testCid) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } + err := suite.client.RemoveCid(testCid) + require.NoError(suite.T(), err) } -func TestRemoveCid_Error(t *testing.T) { +func (suite *CodexClientTestSuite) TestRemoveCid_Error() { const testCid = "zDvZRwzmTestCID" - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + suite.server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Return error status w.WriteHeader(http.StatusInternalServerError) w.Write([]byte("server error")) })) - defer server.Close() - client := communities.NewCodexClient("localhost", "8080") - client.BaseURL = server.URL + suite.client.BaseURL = suite.server.URL - err := client.RemoveCid(testCid) - if err == nil { - t.Fatal("expected error, got nil") - } - if !strings.Contains(err.Error(), "500") { - t.Fatalf("error should mention status 500, got: %v", err) - } + err := suite.client.RemoveCid(testCid) + require.Error(suite.T(), err) + assert.Contains(suite.T(), err.Error(), "500", "error should mention status 500") } -func TestTriggerDownload(t *testing.T) { +func (suite *CodexClientTestSuite) TestTriggerDownload() { const testCid = "zDvZRwzmTestCID" const expectedManifest = `{ "cid": "zDvZRwzmTestCID", @@ -292,7 +276,7 @@ func TestTriggerDownload(t *testing.T) { } }` - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + suite.server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { w.WriteHeader(http.StatusMethodNotAllowed) return @@ -305,105 +289,71 @@ func TestTriggerDownload(t *testing.T) { w.WriteHeader(http.StatusOK) w.Write([]byte(expectedManifest)) })) - defer server.Close() - client := communities.NewCodexClient("localhost", "8080") - client.BaseURL = server.URL + suite.client.BaseURL = suite.server.URL ctx := context.Background() - manifest, err := client.TriggerDownloadWithContext(ctx, testCid) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - - if manifest.CID != testCid { - t.Fatalf("expected CID %q, got %q", testCid, manifest.CID) - } - if manifest.Manifest.TreeCid != "zDvZRwzmTreeCID" { - t.Fatalf("expected TreeCid %q, got %q", "zDvZRwzmTreeCID", manifest.Manifest.TreeCid) - } - if manifest.Manifest.DatasetSize != 1024 { - t.Fatalf("expected DatasetSize %d, got %d", 1024, manifest.Manifest.DatasetSize) - } - if manifest.Manifest.Filename != "test-file.bin" { - t.Fatalf("expected Filename %q, got %q", "test-file.bin", manifest.Manifest.Filename) - } + manifest, err := suite.client.TriggerDownloadWithContext(ctx, testCid) + require.NoError(suite.T(), err) + assert.Equal(suite.T(), testCid, manifest.CID) + assert.Equal(suite.T(), "zDvZRwzmTreeCID", manifest.Manifest.TreeCid) + assert.Equal(suite.T(), int64(1024), manifest.Manifest.DatasetSize) + assert.Equal(suite.T(), "test-file.bin", manifest.Manifest.Filename) } -func TestTriggerDownloadWithContext_RequestError(t *testing.T) { +func (suite *CodexClientTestSuite) TestTriggerDownloadWithContext_RequestError() { // Create a server and immediately close it to trigger connection error - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})) - server.Close() + suite.server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})) + suite.server.Close() - client := communities.NewCodexClient("localhost", "8080") - client.BaseURL = server.URL + suite.client.BaseURL = suite.server.URL ctx := context.Background() - manifest, err := client.TriggerDownloadWithContext(ctx, "zDvZRwzmRigWseNB7WqmudkKAPgZmrDCE9u5cY4KvCqhRo9Ki") - if err == nil { - t.Fatal("expected error, got nil") - } - if manifest != nil { - t.Fatalf("expected nil manifest on error, got %v", manifest) - } + manifest, err := suite.client.TriggerDownloadWithContext(ctx, "zDvZRwzmRigWseNB7WqmudkKAPgZmrDCE9u5cY4KvCqhRo9Ki") + require.Error(suite.T(), err) + assert.Nil(suite.T(), manifest, "expected nil manifest on error") } -func TestTriggerDownloadWithContext_JSONParseError(t *testing.T) { +func (suite *CodexClientTestSuite) TestTriggerDownloadWithContext_JSONParseError() { const testCid = "zDvZRwzmTestCID" - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + suite.server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) // Return invalid JSON w.Write([]byte(`{"invalid": json}`)) })) - defer server.Close() - client := communities.NewCodexClient("localhost", "8080") - client.BaseURL = server.URL + suite.client.BaseURL = suite.server.URL ctx := context.Background() - manifest, err := client.TriggerDownloadWithContext(ctx, testCid) - if err == nil { - t.Fatal("expected JSON parse error, got nil") - } - if manifest != nil { - t.Fatalf("expected nil manifest on parse error, got %v", manifest) - } - if !strings.Contains(err.Error(), "failed to parse download manifest") { - t.Fatalf("error should mention parse failure, got: %v", err) - } + manifest, err := suite.client.TriggerDownloadWithContext(ctx, testCid) + require.Error(suite.T(), err, "expected JSON parse error") + assert.Nil(suite.T(), manifest, "expected nil manifest on parse error") + assert.Contains(suite.T(), err.Error(), "failed to parse download manifest", "error should mention parse failure") } -func TestTriggerDownloadWithContext_HTTPError(t *testing.T) { +func (suite *CodexClientTestSuite) TestTriggerDownloadWithContext_HTTPError() { const testCid = "zDvZRwzmTestCID" - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + suite.server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNotFound) w.Write([]byte("CID not found")) })) - defer server.Close() - client := communities.NewCodexClient("localhost", "8080") - client.BaseURL = server.URL + suite.client.BaseURL = suite.server.URL ctx := context.Background() - manifest, err := client.TriggerDownloadWithContext(ctx, testCid) - if err == nil { - t.Fatal("expected error for 404 status, got nil") - } - if manifest != nil { - t.Fatalf("expected nil manifest on HTTP error, got %v", manifest) - } - if !strings.Contains(err.Error(), "404") { - t.Fatalf("error should mention status 404, got: %v", err) - } + manifest, err := suite.client.TriggerDownloadWithContext(ctx, testCid) + require.Error(suite.T(), err, "expected error for 404 status") + assert.Nil(suite.T(), manifest, "expected nil manifest on HTTP error") + assert.Contains(suite.T(), err.Error(), "404", "error should mention status 404") } -func TestTriggerDownloadWithContext_Cancellation(t *testing.T) { +func (suite *CodexClientTestSuite) TestTriggerDownloadWithContext_Cancellation() { const testCid = "zDvZRwzmTestCID" - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + suite.server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Simulate slow response to allow cancellation select { case <-r.Context().Done(): @@ -414,177 +364,132 @@ func TestTriggerDownloadWithContext_Cancellation(t *testing.T) { w.Write([]byte(`{"cid": "test"}`)) } })) - defer server.Close() - client := communities.NewCodexClient("localhost", "8080") - client.BaseURL = server.URL + suite.client.BaseURL = suite.server.URL // Cancel after 50ms (before server responds) ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond) defer cancel() - manifest, err := client.TriggerDownloadWithContext(ctx, testCid) - if err == nil { - t.Fatal("expected cancellation error, got nil") - } - if manifest != nil { - t.Fatalf("expected nil manifest on cancellation, got %v", manifest) - } + manifest, err := suite.client.TriggerDownloadWithContext(ctx, testCid) + require.Error(suite.T(), err, "expected cancellation error") + assert.Nil(suite.T(), manifest, "expected nil manifest on cancellation") // Accept either canceled or deadline exceeded depending on timing if !errors.Is(err, context.Canceled) && !errors.Is(err, context.DeadlineExceeded) { // net/http may wrap the context error; check error string as a fallback es := err.Error() if !(es == context.Canceled.Error() || es == context.DeadlineExceeded.Error()) { - t.Fatalf("expected context cancellation, got: %v", err) + suite.T().Fatalf("expected context cancellation, got: %v", err) } } } -func TestLocalDownload(t *testing.T) { +func (suite *CodexClientTestSuite) TestLocalDownload() { testData := []byte("test data for local download") testCid := "zDvZRwzmTestCID" - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + suite.server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Verify request method and path - if r.Method != "GET" { - t.Errorf("Expected GET request, got %s", r.Method) - } + assert.Equal(suite.T(), "GET", r.Method, "Expected GET request") expectedPath := "/api/codex/v1/data/" + testCid - if r.URL.Path != expectedPath { - t.Errorf("Expected path %s, got %s", expectedPath, r.URL.Path) - } + assert.Equal(suite.T(), expectedPath, r.URL.Path, "Expected correct path") w.WriteHeader(http.StatusOK) w.Write(testData) })) - defer server.Close() - client := communities.NewCodexClient("localhost", "8080") - client.BaseURL = server.URL + suite.client.BaseURL = suite.server.URL var buf bytes.Buffer - err := client.LocalDownload(testCid, &buf) - if err != nil { - t.Fatalf("LocalDownload failed: %v", err) - } - - if !bytes.Equal(buf.Bytes(), testData) { - t.Errorf("Downloaded data mismatch. Expected %q, got %q", string(testData), buf.String()) - } + err := suite.client.LocalDownload(testCid, &buf) + require.NoError(suite.T(), err, "LocalDownload failed") + assert.Equal(suite.T(), testData, buf.Bytes(), "Downloaded data mismatch") } -func TestLocalDownloadWithContext_Success(t *testing.T) { +func (suite *CodexClientTestSuite) TestLocalDownloadWithContext_Success() { testData := []byte("test data for local download with context") testCid := "zDvZRwzmTestCID" - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + suite.server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Verify request method and path - if r.Method != "GET" { - t.Errorf("Expected GET request, got %s", r.Method) - } + assert.Equal(suite.T(), "GET", r.Method, "Expected GET request") expectedPath := "/api/codex/v1/data/" + testCid - if r.URL.Path != expectedPath { - t.Errorf("Expected path %s, got %s", expectedPath, r.URL.Path) - } + assert.Equal(suite.T(), expectedPath, r.URL.Path, "Expected correct path") w.WriteHeader(http.StatusOK) w.Write(testData) })) - defer server.Close() - client := communities.NewCodexClient("localhost", "8080") - client.BaseURL = server.URL + suite.client.BaseURL = suite.server.URL ctx := context.Background() var buf bytes.Buffer - err := client.LocalDownloadWithContext(ctx, testCid, &buf) - if err != nil { - t.Fatalf("LocalDownloadWithContext failed: %v", err) - } - - if !bytes.Equal(buf.Bytes(), testData) { - t.Errorf("Downloaded data mismatch. Expected %q, got %q", string(testData), buf.String()) - } + err := suite.client.LocalDownloadWithContext(ctx, testCid, &buf) + require.NoError(suite.T(), err, "LocalDownloadWithContext failed") + assert.Equal(suite.T(), testData, buf.Bytes(), "Downloaded data mismatch") } -func TestLocalDownloadWithContext_RequestError(t *testing.T) { +func (suite *CodexClientTestSuite) TestLocalDownloadWithContext_RequestError() { // Create a server and immediately close it to trigger connection error - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})) - server.Close() + suite.server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})) + suite.server.Close() - client := communities.NewCodexClient("localhost", "8080") - client.BaseURL = server.URL + suite.client.BaseURL = suite.server.URL ctx := context.Background() var buf bytes.Buffer - err := client.LocalDownloadWithContext(ctx, "zDvZRwzmTestCID", &buf) - if err == nil { - t.Fatal("Expected error due to closed server, got nil") - } - - if !strings.Contains(err.Error(), "failed to download from codex") { - t.Errorf("Expected 'failed to download from codex' in error, got: %v", err) - } + err := suite.client.LocalDownloadWithContext(ctx, "zDvZRwzmTestCID", &buf) + require.Error(suite.T(), err, "Expected error due to closed server") + assert.Contains(suite.T(), err.Error(), "failed to download from codex") } -func TestLocalDownloadWithContext_HTTPError(t *testing.T) { +func (suite *CodexClientTestSuite) TestLocalDownloadWithContext_HTTPError() { testCid := "zDvZRwzmTestCID" - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + suite.server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNotFound) w.Write([]byte("CID not found in local storage")) })) - defer server.Close() - client := communities.NewCodexClient("localhost", "8080") - client.BaseURL = server.URL + suite.client.BaseURL = suite.server.URL ctx := context.Background() var buf bytes.Buffer - err := client.LocalDownloadWithContext(ctx, testCid, &buf) - if err == nil { - t.Fatal("Expected error for HTTP 404, got nil") - } - - if !strings.Contains(err.Error(), "404") { - t.Errorf("Expected '404' in error message, got: %v", err) - } + err := suite.client.LocalDownloadWithContext(ctx, testCid, &buf) + require.Error(suite.T(), err, "Expected error for HTTP 404") + assert.Contains(suite.T(), err.Error(), "404", "Expected '404' in error message") } -func TestLocalDownloadWithContext_Cancellation(t *testing.T) { +func (suite *CodexClientTestSuite) TestLocalDownloadWithContext_Cancellation() { testCid := "zDvZRwzmTestCID" - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + suite.server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Simulate a slow response time.Sleep(100 * time.Millisecond) w.WriteHeader(http.StatusOK) w.Write([]byte("slow response")) })) - defer server.Close() - client := communities.NewCodexClient("localhost", "8080") - client.BaseURL = server.URL + suite.client.BaseURL = suite.server.URL // Create a context with a very short timeout ctx, cancel := context.WithTimeout(context.Background(), 10*time.Millisecond) defer cancel() var buf bytes.Buffer - err := client.LocalDownloadWithContext(ctx, testCid, &buf) - if err == nil { - t.Fatal("Expected context cancellation error, got nil") - } + err := suite.client.LocalDownloadWithContext(ctx, testCid, &buf) + require.Error(suite.T(), err, "Expected context cancellation error") // Accept either canceled or deadline exceeded depending on timing if !errors.Is(err, context.Canceled) && !errors.Is(err, context.DeadlineExceeded) { // net/http may wrap the context error; check error string as a fallback es := err.Error() if !(es == context.Canceled.Error() || es == context.DeadlineExceeded.Error()) { - t.Fatalf("expected context cancellation, got: %v", err) + suite.T().Fatalf("expected context cancellation, got: %v", err) } } } -func TestFetchManifestWithContext_Success(t *testing.T) { +func (suite *CodexClientTestSuite) TestFetchManifestWithContext_Success() { testCid := "zDvZRwzmTestCID" expectedManifest := `{ "cid": "zDvZRwzmTestCID", @@ -598,173 +503,108 @@ func TestFetchManifestWithContext_Success(t *testing.T) { } }` - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodGet { - w.WriteHeader(http.StatusMethodNotAllowed) - return - } + suite.server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assert.Equal(suite.T(), http.MethodGet, r.Method) expectedPath := fmt.Sprintf("/api/codex/v1/data/%s/network/manifest", testCid) - if r.URL.Path != expectedPath { - w.WriteHeader(http.StatusNotFound) - return - } + assert.Equal(suite.T(), expectedPath, r.URL.Path) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) w.Write([]byte(expectedManifest)) })) - defer server.Close() - client := communities.NewCodexClient("localhost", "8080") - client.BaseURL = server.URL + suite.client.BaseURL = suite.server.URL ctx := context.Background() - manifest, err := client.FetchManifestWithContext(ctx, testCid) - if err != nil { - t.Fatalf("Expected no error, got: %v", err) - } + manifest, err := suite.client.FetchManifestWithContext(ctx, testCid) + require.NoError(suite.T(), err, "Expected no error") + require.NotNil(suite.T(), manifest, "Expected manifest, got nil") - if manifest == nil { - t.Fatal("Expected manifest, got nil") - } - - if manifest.CID != testCid { - t.Errorf("Expected CID %s, got %s", testCid, manifest.CID) - } - - if manifest.Manifest.TreeCid != "zDvZRwzmTreeCID123" { - t.Errorf("Expected TreeCid %s, got %s", "zDvZRwzmTreeCID123", manifest.Manifest.TreeCid) - } - - if manifest.Manifest.DatasetSize != 1024 { - t.Errorf("Expected DatasetSize %d, got %d", 1024, manifest.Manifest.DatasetSize) - } - - if manifest.Manifest.BlockSize != 256 { - t.Errorf("Expected BlockSize %d, got %d", 256, manifest.Manifest.BlockSize) - } - - if !manifest.Manifest.Protected { - t.Error("Expected Protected to be true, got false") - } - - if manifest.Manifest.Filename != "test-file.bin" { - t.Errorf("Expected Filename %s, got %s", "test-file.bin", manifest.Manifest.Filename) - } - - if manifest.Manifest.Mimetype != "application/octet-stream" { - t.Errorf("Expected Mimetype %s, got %s", "application/octet-stream", manifest.Manifest.Mimetype) - } + assert.Equal(suite.T(), testCid, manifest.CID) + assert.Equal(suite.T(), "zDvZRwzmTreeCID123", manifest.Manifest.TreeCid) + assert.Equal(suite.T(), int64(1024), manifest.Manifest.DatasetSize) + assert.Equal(suite.T(), 256, manifest.Manifest.BlockSize) + assert.True(suite.T(), manifest.Manifest.Protected, "Expected Protected to be true") + assert.Equal(suite.T(), "test-file.bin", manifest.Manifest.Filename) + assert.Equal(suite.T(), "application/octet-stream", manifest.Manifest.Mimetype) } -func TestFetchManifestWithContext_RequestError(t *testing.T) { +func (suite *CodexClientTestSuite) TestFetchManifestWithContext_RequestError() { // Create a server and immediately close it to trigger connection error - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})) - server.Close() + suite.server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})) + suite.server.Close() - client := communities.NewCodexClient("localhost", "8080") - client.BaseURL = server.URL + suite.client.BaseURL = suite.server.URL ctx := context.Background() - manifest, err := client.FetchManifestWithContext(ctx, "test-cid") - if err == nil { - t.Fatal("Expected error for closed server, got nil") - } - if manifest != nil { - t.Fatal("Expected nil manifest on error, got non-nil") - } - - if !strings.Contains(err.Error(), "failed to fetch manifest from codex") { - t.Errorf("Expected 'failed to fetch manifest from codex' in error message, got: %v", err) - } + manifest, err := suite.client.FetchManifestWithContext(ctx, "test-cid") + require.Error(suite.T(), err, "Expected error for closed server") + assert.Nil(suite.T(), manifest, "Expected nil manifest on error") + assert.Contains(suite.T(), err.Error(), "failed to fetch manifest from codex") } -func TestFetchManifestWithContext_HTTPError(t *testing.T) { +func (suite *CodexClientTestSuite) TestFetchManifestWithContext_HTTPError() { testCid := "zDvZRwzmTestCID" - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + suite.server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNotFound) w.Write([]byte("Manifest not found")) })) - defer server.Close() - client := communities.NewCodexClient("localhost", "8080") - client.BaseURL = server.URL + suite.client.BaseURL = suite.server.URL ctx := context.Background() - manifest, err := client.FetchManifestWithContext(ctx, testCid) - if err == nil { - t.Fatal("Expected error for HTTP 404, got nil") - } - if manifest != nil { - t.Fatal("Expected nil manifest on error, got non-nil") - } - - if !strings.Contains(err.Error(), "404") { - t.Errorf("Expected '404' in error message, got: %v", err) - } + manifest, err := suite.client.FetchManifestWithContext(ctx, testCid) + require.Error(suite.T(), err, "Expected error for HTTP 404") + assert.Nil(suite.T(), manifest, "Expected nil manifest on error") + assert.Contains(suite.T(), err.Error(), "404", "Expected '404' in error message") } -func TestFetchManifestWithContext_JSONParseError(t *testing.T) { +func (suite *CodexClientTestSuite) TestFetchManifestWithContext_JSONParseError() { testCid := "zDvZRwzmTestCID" - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + suite.server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) w.Write([]byte("invalid json {")) })) - defer server.Close() - client := communities.NewCodexClient("localhost", "8080") - client.BaseURL = server.URL + suite.client.BaseURL = suite.server.URL ctx := context.Background() - manifest, err := client.FetchManifestWithContext(ctx, testCid) - if err == nil { - t.Fatal("Expected error for invalid JSON, got nil") - } - if manifest != nil { - t.Fatal("Expected nil manifest on JSON parse error, got non-nil") - } - - if !strings.Contains(err.Error(), "failed to parse manifest") { - t.Errorf("Expected 'failed to parse manifest' in error message, got: %v", err) - } + manifest, err := suite.client.FetchManifestWithContext(ctx, testCid) + require.Error(suite.T(), err, "Expected error for invalid JSON") + assert.Nil(suite.T(), manifest, "Expected nil manifest on JSON parse error") + assert.Contains(suite.T(), err.Error(), "failed to parse manifest", "Expected 'failed to parse manifest' in error message") } -func TestFetchManifestWithContext_Cancellation(t *testing.T) { +func (suite *CodexClientTestSuite) TestFetchManifestWithContext_Cancellation() { testCid := "zDvZRwzmTestCID" - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + suite.server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Simulate a slow response time.Sleep(100 * time.Millisecond) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) w.Write([]byte(`{"cid": "test"}`)) })) - defer server.Close() - client := communities.NewCodexClient("localhost", "8080") - client.BaseURL = server.URL + suite.client.BaseURL = suite.server.URL // Create a context with a very short timeout ctx, cancel := context.WithTimeout(context.Background(), 10*time.Millisecond) defer cancel() - manifest, err := client.FetchManifestWithContext(ctx, testCid) - if err == nil { - t.Fatal("Expected context cancellation error, got nil") - } - if manifest != nil { - t.Fatal("Expected nil manifest on cancellation, got non-nil") - } + manifest, err := suite.client.FetchManifestWithContext(ctx, testCid) + require.Error(suite.T(), err, "Expected context cancellation error") + assert.Nil(suite.T(), manifest, "Expected nil manifest on cancellation") // Accept either canceled or deadline exceeded depending on timing if !errors.Is(err, context.Canceled) && !errors.Is(err, context.DeadlineExceeded) { // net/http may wrap the context error; check error string as a fallback es := err.Error() if !(es == context.Canceled.Error() || es == context.DeadlineExceeded.Error()) { - t.Fatalf("expected context cancellation, got: %v", err) + suite.T().Fatalf("expected context cancellation, got: %v", err) } } }