mirror of
https://github.com/logos-storage/logos-storage-go.git
synced 2026-04-18 08:53:13 +00:00
adds "HasCid" and "RemoveCid" methods to CodexClient
This commit is contained in:
parent
bdde71e871
commit
754215e9f7
@ -9,6 +9,7 @@ package communities
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
@ -76,6 +77,57 @@ func (c *CodexClient) LocalDownload(cid string, output io.Writer) error {
|
||||
return c.LocalDownloadWithContext(context.Background(), cid, output)
|
||||
}
|
||||
|
||||
func (c *CodexClient) HasCid(cid string) (bool, error) {
|
||||
url := fmt.Sprintf("%s/api/codex/v1/data/%s/has", c.BaseURL, cid)
|
||||
|
||||
resp, err := c.Client.Get(url)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to check cid existence: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
return false, fmt.Errorf("cid check failed with status %d: %s", resp.StatusCode, string(body))
|
||||
}
|
||||
|
||||
// Parse JSON response: {"<cid>": <bool>}
|
||||
var result map[string]bool
|
||||
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
|
||||
return false, fmt.Errorf("failed to parse response: %w", err)
|
||||
}
|
||||
|
||||
// Validate the CID key matches request
|
||||
hasCid, exists := result[cid]
|
||||
if !exists {
|
||||
return false, fmt.Errorf("response missing CID key %q", cid)
|
||||
}
|
||||
|
||||
return hasCid, nil
|
||||
}
|
||||
|
||||
func (c *CodexClient) RemoveCid(cid string) error {
|
||||
url := fmt.Sprintf("%s/api/codex/v1/data/%s", c.BaseURL, cid)
|
||||
|
||||
req, err := http.NewRequest("DELETE", url, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create request: %w", err)
|
||||
}
|
||||
|
||||
resp, err := c.Client.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed trying to delete cid: %s, %w", cid, err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusNoContent {
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
return fmt.Errorf("cid delete failed with status %d: %s", resp.StatusCode, string(body))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DownloadWithContext downloads data from Codex by CID with cancellation support
|
||||
func (c *CodexClient) DownloadWithContext(ctx context.Context, cid string, output io.Writer) error {
|
||||
url := fmt.Sprintf("%s/api/codex/v1/data/%s/network/stream", c.BaseURL, cid)
|
||||
|
||||
@ -49,6 +49,16 @@ func TestIntegration_UploadAndDownload(t *testing.T) {
|
||||
}
|
||||
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)
|
||||
|
||||
// Download via network stream with a context timeout to avoid hanging
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
@ -62,6 +72,50 @@ func TestIntegration_UploadAndDownload(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestIntegration_CheckNonExistingCID(t *testing.T) {
|
||||
host := getenv("CODEX_HOST", "localhost")
|
||||
port := getenv("CODEX_API_PORT", "8080")
|
||||
client := NewCodexClient(host, port)
|
||||
|
||||
// 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]))
|
||||
|
||||
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)
|
||||
|
||||
// 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)
|
||||
|
||||
// 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)
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
func getenv(k, def string) string {
|
||||
if v := os.Getenv(k); v != "" {
|
||||
return v
|
||||
|
||||
@ -4,9 +4,11 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
@ -141,3 +143,135 @@ func TestDownloadWithContext_Cancel(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHasCid_Success(t *testing.T) {
|
||||
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 {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path != "/api/codex/v1/data/"+tt.cid+"/has" {
|
||||
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)
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
client := NewCodexClient("localhost", "8080")
|
||||
client.BaseURL = 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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHasCid_RequestError(t *testing.T) {
|
||||
// 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
|
||||
|
||||
client := NewCodexClient("localhost", "8080")
|
||||
client.BaseURL = 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)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHasCid_CidMismatch(t *testing.T) {
|
||||
const requestCid = "zDvZRwzmRequestCID"
|
||||
const responseCid = "zDvZRwzmDifferentCID"
|
||||
|
||||
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 := NewCodexClient("localhost", "8080")
|
||||
client.BaseURL = 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)
|
||||
}
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveCid_Success(t *testing.T) {
|
||||
const testCid = "zDvZRwzmTestCID"
|
||||
|
||||
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)
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
client := NewCodexClient("localhost", "8080")
|
||||
client.BaseURL = server.URL
|
||||
|
||||
err := client.RemoveCid(testCid)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveCid_Error(t *testing.T) {
|
||||
const testCid = "zDvZRwzmTestCID"
|
||||
|
||||
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 := NewCodexClient("localhost", "8080")
|
||||
client.BaseURL = 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)
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user