mirror of
https://github.com/logos-storage/logos-storage-go.git
synced 2026-01-02 21:33:11 +00:00
611 lines
20 KiB
Go
611 lines
20 KiB
Go
package communities_test
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"github.com/stretchr/testify/suite"
|
|
|
|
"go-codex-client/communities"
|
|
)
|
|
|
|
// 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
|
|
suite.server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
if r.URL.Path != "/api/codex/v1/data" {
|
|
w.WriteHeader(http.StatusNotFound)
|
|
return
|
|
}
|
|
|
|
if ct := r.Header.Get("Content-Type"); ct != "application/octet-stream" {
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
return
|
|
}
|
|
if cd := r.Header.Get("Content-Disposition"); cd != "filename=\"hello.txt\"" {
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
_, _ = io.ReadAll(r.Body) // consume body
|
|
_ = r.Body.Close()
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
// Codex returns CIDv1 base58btc
|
|
// prefix: zDv
|
|
// - z = multibase prefix for base58btc
|
|
// - Dv = CIDv1 prefix for raw codex
|
|
// we add a newline to simulate real response
|
|
_, _ = w.Write([]byte("zDvZRwzmTestCID123\n"))
|
|
}))
|
|
|
|
suite.client.BaseURL = suite.server.URL
|
|
|
|
// Act
|
|
cid, err := suite.client.Upload(bytes.NewReader([]byte("payload")), "hello.txt")
|
|
|
|
// Assert
|
|
require.NoError(suite.T(), err)
|
|
// Codex uses CIDv1 with base58btc encoding (prefix: zDv)
|
|
assert.Equal(suite.T(), "zDvZRwzmTestCID123", cid)
|
|
}
|
|
|
|
func (suite *CodexClientTestSuite) TestDownload_Success() {
|
|
const wantCID = "zDvZRwzm"
|
|
const payload = "hello from codex"
|
|
|
|
suite.server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodGet {
|
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
if r.URL.Path != "/api/codex/v1/data/"+wantCID+"/network/stream" {
|
|
w.WriteHeader(http.StatusNotFound)
|
|
return
|
|
}
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
_, _ = w.Write([]byte(payload))
|
|
}))
|
|
|
|
suite.client.BaseURL = suite.server.URL
|
|
|
|
var buf bytes.Buffer
|
|
err := suite.client.Download(wantCID, &buf)
|
|
require.NoError(suite.T(), err)
|
|
assert.Equal(suite.T(), payload, buf.String())
|
|
}
|
|
|
|
func (suite *CodexClientTestSuite) TestDownloadWithContext_Cancel() {
|
|
const cid = "zDvZRwzm"
|
|
|
|
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
|
|
}
|
|
w.Header().Set("Content-Type", "application/octet-stream")
|
|
flusher, _ := w.(http.Flusher)
|
|
w.WriteHeader(http.StatusOK)
|
|
// Stream data slowly so the request can be canceled
|
|
for i := 0; i < 1000; i++ {
|
|
select {
|
|
case <-r.Context().Done():
|
|
return
|
|
default:
|
|
}
|
|
if _, err := w.Write([]byte("x")); err != nil {
|
|
// Client likely went away; stop writing
|
|
return
|
|
}
|
|
if flusher != nil {
|
|
flusher.Flush()
|
|
}
|
|
time.Sleep(10 * time.Millisecond)
|
|
}
|
|
}))
|
|
|
|
suite.client.BaseURL = suite.server.URL
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Millisecond)
|
|
defer cancel()
|
|
|
|
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()) {
|
|
suite.T().Fatalf("expected context cancellation, got: %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (suite *CodexClientTestSuite) TestHasCid_Success() {
|
|
tests := []struct {
|
|
name string
|
|
cid string
|
|
hasIt bool
|
|
wantBool bool
|
|
}{
|
|
{"has CID returns true", "zDvZRwzmTestCID", true, true},
|
|
{"has CID returns false", "zDvZRwzmTestCID", false, false},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
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
|
|
}
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.WriteHeader(http.StatusOK)
|
|
// Return JSON: {"<cid>": <bool>}
|
|
fmt.Fprintf(w, `{"%s": %t}`, tt.cid, tt.hasIt)
|
|
}))
|
|
|
|
suite.client.BaseURL = suite.server.URL
|
|
|
|
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 (suite *CodexClientTestSuite) TestHasCid_RequestError() {
|
|
// Create a server and immediately close it to trigger connection error
|
|
suite.server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
|
|
suite.server.Close() // Close immediately so connection fails
|
|
|
|
suite.client.BaseURL = suite.server.URL // Use the closed server's URL
|
|
|
|
got, err := suite.client.HasCid("zDvZRwzmTestCID")
|
|
require.Error(suite.T(), err)
|
|
assert.False(suite.T(), got, "expected false on error")
|
|
}
|
|
|
|
func (suite *CodexClientTestSuite) TestHasCid_CidMismatch() {
|
|
const requestCid = "zDvZRwzmRequestCID"
|
|
const responseCid = "zDvZRwzmDifferentCID"
|
|
|
|
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)
|
|
}))
|
|
|
|
suite.client.BaseURL = suite.server.URL
|
|
|
|
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
|
|
assert.Contains(suite.T(), err.Error(), requestCid, "error should mention request CID")
|
|
}
|
|
|
|
func (suite *CodexClientTestSuite) TestRemoveCid_Success() {
|
|
const testCid = "zDvZRwzmTestCID"
|
|
|
|
suite.server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodDelete {
|
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
if r.URL.Path != "/api/codex/v1/data/"+testCid {
|
|
w.WriteHeader(http.StatusNotFound)
|
|
return
|
|
}
|
|
// DELETE should return 204 No Content
|
|
w.WriteHeader(http.StatusNoContent)
|
|
}))
|
|
|
|
suite.client.BaseURL = suite.server.URL
|
|
|
|
err := suite.client.RemoveCid(testCid)
|
|
require.NoError(suite.T(), err)
|
|
}
|
|
|
|
func (suite *CodexClientTestSuite) TestRemoveCid_Error() {
|
|
const testCid = "zDvZRwzmTestCID"
|
|
|
|
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"))
|
|
}))
|
|
|
|
suite.client.BaseURL = suite.server.URL
|
|
|
|
err := suite.client.RemoveCid(testCid)
|
|
require.Error(suite.T(), err)
|
|
assert.Contains(suite.T(), err.Error(), "500", "error should mention status 500")
|
|
}
|
|
|
|
func (suite *CodexClientTestSuite) TestTriggerDownload() {
|
|
const testCid = "zDvZRwzmTestCID"
|
|
const expectedManifest = `{
|
|
"cid": "zDvZRwzmTestCID",
|
|
"manifest": {
|
|
"treeCid": "zDvZRwzmTreeCID",
|
|
"datasetSize": 1024,
|
|
"blockSize": 65536,
|
|
"protected": false,
|
|
"filename": "test-file.bin",
|
|
"mimetype": "application/octet-stream"
|
|
}
|
|
}`
|
|
|
|
suite.server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
if r.URL.Path != "/api/codex/v1/data/"+testCid+"/network" {
|
|
w.WriteHeader(http.StatusNotFound)
|
|
return
|
|
}
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.WriteHeader(http.StatusOK)
|
|
w.Write([]byte(expectedManifest))
|
|
}))
|
|
|
|
suite.client.BaseURL = suite.server.URL
|
|
|
|
ctx := context.Background()
|
|
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 (suite *CodexClientTestSuite) TestTriggerDownloadWithContext_RequestError() {
|
|
// Create a server and immediately close it to trigger connection error
|
|
suite.server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
|
|
suite.server.Close()
|
|
|
|
suite.client.BaseURL = suite.server.URL
|
|
|
|
ctx := context.Background()
|
|
manifest, err := suite.client.TriggerDownloadWithContext(ctx, "zDvZRwzmRigWseNB7WqmudkKAPgZmrDCE9u5cY4KvCqhRo9Ki")
|
|
require.Error(suite.T(), err)
|
|
assert.Nil(suite.T(), manifest, "expected nil manifest on error")
|
|
}
|
|
|
|
func (suite *CodexClientTestSuite) TestTriggerDownloadWithContext_JSONParseError() {
|
|
const testCid = "zDvZRwzmTestCID"
|
|
|
|
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}`))
|
|
}))
|
|
|
|
suite.client.BaseURL = suite.server.URL
|
|
|
|
ctx := context.Background()
|
|
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 (suite *CodexClientTestSuite) TestTriggerDownloadWithContext_HTTPError() {
|
|
const testCid = "zDvZRwzmTestCID"
|
|
|
|
suite.server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusNotFound)
|
|
w.Write([]byte("CID not found"))
|
|
}))
|
|
|
|
suite.client.BaseURL = suite.server.URL
|
|
|
|
ctx := context.Background()
|
|
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 (suite *CodexClientTestSuite) TestTriggerDownloadWithContext_Cancellation() {
|
|
const testCid = "zDvZRwzmTestCID"
|
|
|
|
suite.server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
// Simulate slow response to allow cancellation
|
|
select {
|
|
case <-r.Context().Done():
|
|
return
|
|
case <-time.After(200 * time.Millisecond):
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.WriteHeader(http.StatusOK)
|
|
w.Write([]byte(`{"cid": "test"}`))
|
|
}
|
|
}))
|
|
|
|
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 := 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()) {
|
|
suite.T().Fatalf("expected context cancellation, got: %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (suite *CodexClientTestSuite) TestLocalDownload() {
|
|
testData := []byte("test data for local download")
|
|
testCid := "zDvZRwzmTestCID"
|
|
|
|
suite.server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
// Verify request method and path
|
|
assert.Equal(suite.T(), "GET", r.Method, "Expected GET request")
|
|
expectedPath := "/api/codex/v1/data/" + testCid
|
|
assert.Equal(suite.T(), expectedPath, r.URL.Path, "Expected correct path")
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
w.Write(testData)
|
|
}))
|
|
|
|
suite.client.BaseURL = suite.server.URL
|
|
|
|
var buf bytes.Buffer
|
|
err := suite.client.LocalDownload(testCid, &buf)
|
|
require.NoError(suite.T(), err, "LocalDownload failed")
|
|
assert.Equal(suite.T(), testData, buf.Bytes(), "Downloaded data mismatch")
|
|
}
|
|
|
|
func (suite *CodexClientTestSuite) TestLocalDownloadWithContext_Success() {
|
|
testData := []byte("test data for local download with context")
|
|
testCid := "zDvZRwzmTestCID"
|
|
|
|
suite.server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
// Verify request method and path
|
|
assert.Equal(suite.T(), "GET", r.Method, "Expected GET request")
|
|
expectedPath := "/api/codex/v1/data/" + testCid
|
|
assert.Equal(suite.T(), expectedPath, r.URL.Path, "Expected correct path")
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
w.Write(testData)
|
|
}))
|
|
|
|
suite.client.BaseURL = suite.server.URL
|
|
|
|
ctx := context.Background()
|
|
var buf bytes.Buffer
|
|
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 (suite *CodexClientTestSuite) TestLocalDownloadWithContext_RequestError() {
|
|
// Create a server and immediately close it to trigger connection error
|
|
suite.server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
|
|
suite.server.Close()
|
|
|
|
suite.client.BaseURL = suite.server.URL
|
|
|
|
ctx := context.Background()
|
|
var buf bytes.Buffer
|
|
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 (suite *CodexClientTestSuite) TestLocalDownloadWithContext_HTTPError() {
|
|
testCid := "zDvZRwzmTestCID"
|
|
|
|
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"))
|
|
}))
|
|
|
|
suite.client.BaseURL = suite.server.URL
|
|
|
|
ctx := context.Background()
|
|
var buf bytes.Buffer
|
|
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 (suite *CodexClientTestSuite) TestLocalDownloadWithContext_Cancellation() {
|
|
testCid := "zDvZRwzmTestCID"
|
|
|
|
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"))
|
|
}))
|
|
|
|
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 := 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()) {
|
|
suite.T().Fatalf("expected context cancellation, got: %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (suite *CodexClientTestSuite) TestFetchManifestWithContext_Success() {
|
|
testCid := "zDvZRwzmTestCID"
|
|
expectedManifest := `{
|
|
"cid": "zDvZRwzmTestCID",
|
|
"manifest": {
|
|
"treeCid": "zDvZRwzmTreeCID123",
|
|
"datasetSize": 1024,
|
|
"blockSize": 256,
|
|
"protected": true,
|
|
"filename": "test-file.bin",
|
|
"mimetype": "application/octet-stream"
|
|
}
|
|
}`
|
|
|
|
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)
|
|
assert.Equal(suite.T(), expectedPath, r.URL.Path)
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.WriteHeader(http.StatusOK)
|
|
w.Write([]byte(expectedManifest))
|
|
}))
|
|
|
|
suite.client.BaseURL = suite.server.URL
|
|
|
|
ctx := context.Background()
|
|
manifest, err := suite.client.FetchManifestWithContext(ctx, testCid)
|
|
require.NoError(suite.T(), err, "Expected no error")
|
|
require.NotNil(suite.T(), manifest, "Expected manifest, got nil")
|
|
|
|
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 (suite *CodexClientTestSuite) TestFetchManifestWithContext_RequestError() {
|
|
// Create a server and immediately close it to trigger connection error
|
|
suite.server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
|
|
suite.server.Close()
|
|
|
|
suite.client.BaseURL = suite.server.URL
|
|
|
|
ctx := context.Background()
|
|
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 (suite *CodexClientTestSuite) TestFetchManifestWithContext_HTTPError() {
|
|
testCid := "zDvZRwzmTestCID"
|
|
|
|
suite.server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusNotFound)
|
|
w.Write([]byte("Manifest not found"))
|
|
}))
|
|
|
|
suite.client.BaseURL = suite.server.URL
|
|
|
|
ctx := context.Background()
|
|
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 (suite *CodexClientTestSuite) TestFetchManifestWithContext_JSONParseError() {
|
|
testCid := "zDvZRwzmTestCID"
|
|
|
|
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 {"))
|
|
}))
|
|
|
|
suite.client.BaseURL = suite.server.URL
|
|
|
|
ctx := context.Background()
|
|
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 (suite *CodexClientTestSuite) TestFetchManifestWithContext_Cancellation() {
|
|
testCid := "zDvZRwzmTestCID"
|
|
|
|
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"}`))
|
|
}))
|
|
|
|
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 := 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()) {
|
|
suite.T().Fatalf("expected context cancellation, got: %v", err)
|
|
}
|
|
}
|
|
}
|