You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
755 lines
20 KiB
755 lines
20 KiB
package blossom |
|
|
|
import ( |
|
"bytes" |
|
"encoding/json" |
|
"io" |
|
"net/http" |
|
"net/http/httptest" |
|
"strings" |
|
"testing" |
|
|
|
"next.orly.dev/pkg/encoders/event" |
|
"next.orly.dev/pkg/encoders/hex" |
|
"next.orly.dev/pkg/encoders/tag" |
|
"next.orly.dev/pkg/encoders/timestamp" |
|
) |
|
|
|
// TestHTTPGetBlob tests GET /<sha256> endpoint |
|
func TestHTTPGetBlob(t *testing.T) { |
|
server, cleanup := testSetup(t) |
|
defer cleanup() |
|
|
|
// Upload a blob first |
|
testData := []byte("test blob content") |
|
sha256Hash := CalculateSHA256(testData) |
|
pubkey := []byte("testpubkey123456789012345678901234") |
|
|
|
err := server.storage.SaveBlob(sha256Hash, testData, pubkey, "text/plain", "") |
|
if err != nil { |
|
t.Fatalf("Failed to save blob: %v", err) |
|
} |
|
|
|
sha256Hex := hex.Enc(sha256Hash) |
|
|
|
// Test GET request |
|
req := httptest.NewRequest("GET", "/"+sha256Hex, nil) |
|
w := httptest.NewRecorder() |
|
server.Handler().ServeHTTP(w, req) |
|
|
|
if w.Code != http.StatusOK { |
|
t.Errorf("Expected status 200, got %d: %s", w.Code, w.Body.String()) |
|
} |
|
|
|
body := w.Body.Bytes() |
|
if !bytes.Equal(body, testData) { |
|
t.Error("Response body mismatch") |
|
} |
|
|
|
if w.Header().Get("Content-Type") != "text/plain" { |
|
t.Errorf("Expected Content-Type text/plain, got %s", w.Header().Get("Content-Type")) |
|
} |
|
} |
|
|
|
// TestHTTPHeadBlob tests HEAD /<sha256> endpoint |
|
func TestHTTPHeadBlob(t *testing.T) { |
|
server, cleanup := testSetup(t) |
|
defer cleanup() |
|
|
|
testData := []byte("test blob content") |
|
sha256Hash := CalculateSHA256(testData) |
|
pubkey := []byte("testpubkey123456789012345678901234") |
|
|
|
err := server.storage.SaveBlob(sha256Hash, testData, pubkey, "text/plain", "") |
|
if err != nil { |
|
t.Fatalf("Failed to save blob: %v", err) |
|
} |
|
|
|
sha256Hex := hex.Enc(sha256Hash) |
|
|
|
req := httptest.NewRequest("HEAD", "/"+sha256Hex, nil) |
|
w := httptest.NewRecorder() |
|
server.Handler().ServeHTTP(w, req) |
|
|
|
if w.Code != http.StatusOK { |
|
t.Errorf("Expected status 200, got %d", w.Code) |
|
} |
|
|
|
if w.Body.Len() != 0 { |
|
t.Error("HEAD request should not return body") |
|
} |
|
|
|
if w.Header().Get("Content-Length") != "17" { |
|
t.Errorf("Expected Content-Length 17, got %s", w.Header().Get("Content-Length")) |
|
} |
|
} |
|
|
|
// TestHTTPUpload tests PUT /upload endpoint |
|
func TestHTTPUpload(t *testing.T) { |
|
server, cleanup := testSetup(t) |
|
defer cleanup() |
|
|
|
_, signer := createTestKeypair(t) |
|
|
|
testData := []byte("test upload data") |
|
sha256Hash := CalculateSHA256(testData) |
|
|
|
// Create auth event |
|
authEv := createAuthEvent(t, signer, "upload", sha256Hash, 3600) |
|
|
|
// Create request |
|
req := httptest.NewRequest("PUT", "/upload", bytes.NewReader(testData)) |
|
req.Header.Set("Authorization", createAuthHeader(authEv)) |
|
req.Header.Set("Content-Type", "text/plain") |
|
|
|
w := httptest.NewRecorder() |
|
server.Handler().ServeHTTP(w, req) |
|
|
|
if w.Code != http.StatusOK { |
|
t.Errorf("Expected status 200, got %d: %s", w.Code, w.Body.String()) |
|
} |
|
|
|
// Parse response |
|
var desc BlobDescriptor |
|
if err := json.Unmarshal(w.Body.Bytes(), &desc); err != nil { |
|
t.Fatalf("Failed to parse response: %v", err) |
|
} |
|
|
|
if desc.SHA256 != hex.Enc(sha256Hash) { |
|
t.Errorf("SHA256 mismatch: expected %s, got %s", hex.Enc(sha256Hash), desc.SHA256) |
|
} |
|
|
|
if desc.Size != int64(len(testData)) { |
|
t.Errorf("Size mismatch: expected %d, got %d", len(testData), desc.Size) |
|
} |
|
|
|
// Verify blob was saved |
|
exists, err := server.storage.HasBlob(sha256Hash) |
|
if err != nil { |
|
t.Fatalf("Failed to check blob: %v", err) |
|
} |
|
if !exists { |
|
t.Error("Blob should exist after upload") |
|
} |
|
} |
|
|
|
// TestHTTPUploadRequirements tests HEAD /upload endpoint |
|
func TestHTTPUploadRequirements(t *testing.T) { |
|
server, cleanup := testSetup(t) |
|
defer cleanup() |
|
|
|
testData := []byte("test data") |
|
sha256Hash := CalculateSHA256(testData) |
|
|
|
req := httptest.NewRequest("HEAD", "/upload", nil) |
|
req.Header.Set("X-SHA-256", hex.Enc(sha256Hash)) |
|
req.Header.Set("X-Content-Length", "9") |
|
req.Header.Set("X-Content-Type", "text/plain") |
|
|
|
w := httptest.NewRecorder() |
|
server.Handler().ServeHTTP(w, req) |
|
|
|
if w.Code != http.StatusOK { |
|
t.Errorf("Expected status 200, got %d: %s", w.Code, w.Header().Get("X-Reason")) |
|
} |
|
} |
|
|
|
// TestHTTPUploadTooLarge tests upload size limit |
|
func TestHTTPUploadTooLarge(t *testing.T) { |
|
server, cleanup := testSetup(t) |
|
defer cleanup() |
|
|
|
// Create request with size exceeding limit |
|
req := httptest.NewRequest("HEAD", "/upload", nil) |
|
req.Header.Set("X-SHA-256", hex.Enc(CalculateSHA256([]byte("test")))) |
|
req.Header.Set("X-Content-Length", "200000000") // 200MB |
|
req.Header.Set("X-Content-Type", "application/octet-stream") |
|
|
|
w := httptest.NewRecorder() |
|
server.Handler().ServeHTTP(w, req) |
|
|
|
if w.Code != http.StatusRequestEntityTooLarge { |
|
t.Errorf("Expected status 413, got %d", w.Code) |
|
} |
|
} |
|
|
|
// TestHTTPListBlobs tests GET /list/<pubkey> endpoint |
|
func TestHTTPListBlobs(t *testing.T) { |
|
server, cleanup := testSetup(t) |
|
defer cleanup() |
|
|
|
_, signer := createTestKeypair(t) |
|
pubkey := signer.Pub() |
|
pubkeyHex := hex.Enc(pubkey) |
|
|
|
// Upload multiple blobs |
|
for i := 0; i < 3; i++ { |
|
testData := []byte("test data " + string(rune('A'+i))) |
|
sha256Hash := CalculateSHA256(testData) |
|
err := server.storage.SaveBlob(sha256Hash, testData, pubkey, "text/plain", "") |
|
if err != nil { |
|
t.Fatalf("Failed to save blob: %v", err) |
|
} |
|
} |
|
|
|
// Create auth event |
|
authEv := createAuthEvent(t, signer, "list", nil, 3600) |
|
|
|
req := httptest.NewRequest("GET", "/list/"+pubkeyHex, nil) |
|
req.Header.Set("Authorization", createAuthHeader(authEv)) |
|
|
|
w := httptest.NewRecorder() |
|
server.Handler().ServeHTTP(w, req) |
|
|
|
if w.Code != http.StatusOK { |
|
t.Errorf("Expected status 200, got %d: %s", w.Code, w.Body.String()) |
|
} |
|
|
|
var descriptors []BlobDescriptor |
|
if err := json.Unmarshal(w.Body.Bytes(), &descriptors); err != nil { |
|
t.Fatalf("Failed to parse response: %v", err) |
|
} |
|
|
|
if len(descriptors) != 3 { |
|
t.Errorf("Expected 3 blobs, got %d", len(descriptors)) |
|
} |
|
} |
|
|
|
// TestHTTPDeleteBlob tests DELETE /<sha256> endpoint |
|
func TestHTTPDeleteBlob(t *testing.T) { |
|
server, cleanup := testSetup(t) |
|
defer cleanup() |
|
|
|
_, signer := createTestKeypair(t) |
|
pubkey := signer.Pub() |
|
|
|
testData := []byte("test delete data") |
|
sha256Hash := CalculateSHA256(testData) |
|
|
|
// Upload blob first |
|
err := server.storage.SaveBlob(sha256Hash, testData, pubkey, "text/plain", "") |
|
if err != nil { |
|
t.Fatalf("Failed to save blob: %v", err) |
|
} |
|
|
|
// Create auth event |
|
authEv := createAuthEvent(t, signer, "delete", sha256Hash, 3600) |
|
|
|
sha256Hex := hex.Enc(sha256Hash) |
|
req := httptest.NewRequest("DELETE", "/"+sha256Hex, nil) |
|
req.Header.Set("Authorization", createAuthHeader(authEv)) |
|
|
|
w := httptest.NewRecorder() |
|
server.Handler().ServeHTTP(w, req) |
|
|
|
if w.Code != http.StatusOK { |
|
t.Errorf("Expected status 200, got %d: %s", w.Code, w.Body.String()) |
|
} |
|
|
|
// Verify blob was deleted |
|
exists, err := server.storage.HasBlob(sha256Hash) |
|
if err != nil { |
|
t.Fatalf("Failed to check blob: %v", err) |
|
} |
|
if exists { |
|
t.Error("Blob should not exist after delete") |
|
} |
|
} |
|
|
|
// TestHTTPMirror tests PUT /mirror endpoint |
|
func TestHTTPMirror(t *testing.T) { |
|
server, cleanup := testSetup(t) |
|
defer cleanup() |
|
|
|
_, signer := createTestKeypair(t) |
|
|
|
// Create a mock remote server |
|
testData := []byte("mirrored blob data") |
|
sha256Hash := CalculateSHA256(testData) |
|
sha256Hex := hex.Enc(sha256Hash) |
|
|
|
mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
|
w.Header().Set("Content-Type", "text/plain") |
|
w.Write(testData) |
|
})) |
|
defer mockServer.Close() |
|
|
|
// Create mirror request |
|
mirrorReq := map[string]string{ |
|
"url": mockServer.URL + "/" + sha256Hex, |
|
} |
|
reqBody, _ := json.Marshal(mirrorReq) |
|
|
|
authEv := createAuthEvent(t, signer, "upload", sha256Hash, 3600) |
|
|
|
req := httptest.NewRequest("PUT", "/mirror", bytes.NewReader(reqBody)) |
|
req.Header.Set("Authorization", createAuthHeader(authEv)) |
|
req.Header.Set("Content-Type", "application/json") |
|
|
|
w := httptest.NewRecorder() |
|
server.Handler().ServeHTTP(w, req) |
|
|
|
if w.Code != http.StatusOK { |
|
t.Errorf("Expected status 200, got %d: %s", w.Code, w.Body.String()) |
|
} |
|
|
|
// Verify blob was saved |
|
exists, err := server.storage.HasBlob(sha256Hash) |
|
if err != nil { |
|
t.Fatalf("Failed to check blob: %v", err) |
|
} |
|
if !exists { |
|
t.Error("Blob should exist after mirror") |
|
} |
|
} |
|
|
|
// TestHTTPMediaUpload tests PUT /media endpoint |
|
func TestHTTPMediaUpload(t *testing.T) { |
|
server, cleanup := testSetup(t) |
|
defer cleanup() |
|
|
|
_, signer := createTestKeypair(t) |
|
|
|
testData := []byte("test media data") |
|
sha256Hash := CalculateSHA256(testData) |
|
|
|
authEv := createAuthEvent(t, signer, "media", sha256Hash, 3600) |
|
|
|
req := httptest.NewRequest("PUT", "/media", bytes.NewReader(testData)) |
|
req.Header.Set("Authorization", createAuthHeader(authEv)) |
|
req.Header.Set("Content-Type", "image/png") |
|
|
|
w := httptest.NewRecorder() |
|
server.Handler().ServeHTTP(w, req) |
|
|
|
if w.Code != http.StatusOK { |
|
t.Errorf("Expected status 200, got %d: %s", w.Code, w.Body.String()) |
|
} |
|
|
|
var desc BlobDescriptor |
|
if err := json.Unmarshal(w.Body.Bytes(), &desc); err != nil { |
|
t.Fatalf("Failed to parse response: %v", err) |
|
} |
|
|
|
if desc.SHA256 == "" { |
|
t.Error("Expected SHA256 in response") |
|
} |
|
} |
|
|
|
// TestHTTPReport tests PUT /report endpoint |
|
func TestHTTPReport(t *testing.T) { |
|
server, cleanup := testSetup(t) |
|
defer cleanup() |
|
|
|
_, signer := createTestKeypair(t) |
|
pubkey := signer.Pub() |
|
|
|
// Upload a blob first |
|
testData := []byte("test blob") |
|
sha256Hash := CalculateSHA256(testData) |
|
|
|
err := server.storage.SaveBlob(sha256Hash, testData, pubkey, "text/plain", "") |
|
if err != nil { |
|
t.Fatalf("Failed to save blob: %v", err) |
|
} |
|
|
|
// Create report event (kind 1984) |
|
reportEv := &event.E{ |
|
CreatedAt: timestamp.Now().V, |
|
Kind: 1984, |
|
Tags: tag.NewS(tag.NewFromAny("x", hex.Enc(sha256Hash))), |
|
Content: []byte("This blob violates policy"), |
|
Pubkey: pubkey, |
|
} |
|
|
|
if err := reportEv.Sign(signer); err != nil { |
|
t.Fatalf("Failed to sign report: %v", err) |
|
} |
|
|
|
reqBody := reportEv.Serialize() |
|
req := httptest.NewRequest("PUT", "/report", bytes.NewReader(reqBody)) |
|
req.Header.Set("Content-Type", "application/json") |
|
|
|
w := httptest.NewRecorder() |
|
server.Handler().ServeHTTP(w, req) |
|
|
|
if w.Code != http.StatusOK { |
|
t.Errorf("Expected status 200, got %d: %s", w.Code, w.Body.String()) |
|
} |
|
} |
|
|
|
// TestHTTPRangeRequest tests range request support |
|
func TestHTTPRangeRequest(t *testing.T) { |
|
server, cleanup := testSetup(t) |
|
defer cleanup() |
|
|
|
testData := []byte("0123456789abcdef") |
|
sha256Hash := CalculateSHA256(testData) |
|
pubkey := []byte("testpubkey123456789012345678901234") |
|
|
|
err := server.storage.SaveBlob(sha256Hash, testData, pubkey, "text/plain", "") |
|
if err != nil { |
|
t.Fatalf("Failed to save blob: %v", err) |
|
} |
|
|
|
sha256Hex := hex.Enc(sha256Hash) |
|
|
|
// Test range request |
|
req := httptest.NewRequest("GET", "/"+sha256Hex, nil) |
|
req.Header.Set("Range", "bytes=4-9") |
|
|
|
w := httptest.NewRecorder() |
|
server.Handler().ServeHTTP(w, req) |
|
|
|
if w.Code != http.StatusPartialContent { |
|
t.Errorf("Expected status 206, got %d", w.Code) |
|
} |
|
|
|
body := w.Body.Bytes() |
|
expected := testData[4:10] |
|
if !bytes.Equal(body, expected) { |
|
t.Errorf("Range response mismatch: expected %s, got %s", string(expected), string(body)) |
|
} |
|
|
|
if w.Header().Get("Content-Range") == "" { |
|
t.Error("Missing Content-Range header") |
|
} |
|
} |
|
|
|
// TestHTTPNotFound tests 404 handling |
|
func TestHTTPNotFound(t *testing.T) { |
|
server, cleanup := testSetup(t) |
|
defer cleanup() |
|
|
|
req := httptest.NewRequest("GET", "/0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", nil) |
|
w := httptest.NewRecorder() |
|
server.Handler().ServeHTTP(w, req) |
|
|
|
if w.Code != http.StatusNotFound { |
|
t.Errorf("Expected status 404, got %d", w.Code) |
|
} |
|
} |
|
|
|
// TestHTTPServerIntegration tests full server integration |
|
func TestHTTPServerIntegration(t *testing.T) { |
|
server, cleanup := testSetup(t) |
|
defer cleanup() |
|
|
|
// Start HTTP server |
|
httpServer := httptest.NewServer(server.Handler()) |
|
defer httpServer.Close() |
|
|
|
_, signer := createTestKeypair(t) |
|
|
|
// Upload blob via HTTP |
|
testData := []byte("integration test data") |
|
sha256Hash := CalculateSHA256(testData) |
|
sha256Hex := hex.Enc(sha256Hash) |
|
|
|
authEv := createAuthEvent(t, signer, "upload", sha256Hash, 3600) |
|
|
|
uploadReq, _ := http.NewRequest("PUT", httpServer.URL+"/upload", bytes.NewReader(testData)) |
|
uploadReq.Header.Set("Authorization", createAuthHeader(authEv)) |
|
uploadReq.Header.Set("Content-Type", "text/plain") |
|
|
|
client := &http.Client{} |
|
resp, err := client.Do(uploadReq) |
|
if err != nil { |
|
t.Fatalf("Failed to upload: %v", err) |
|
} |
|
defer resp.Body.Close() |
|
|
|
if resp.StatusCode != http.StatusOK { |
|
body, _ := io.ReadAll(resp.Body) |
|
t.Fatalf("Upload failed: status %d, body: %s", resp.StatusCode, string(body)) |
|
} |
|
|
|
// Retrieve blob via HTTP |
|
getReq, _ := http.NewRequest("GET", httpServer.URL+"/"+sha256Hex, nil) |
|
getResp, err := client.Do(getReq) |
|
if err != nil { |
|
t.Fatalf("Failed to get blob: %v", err) |
|
} |
|
defer getResp.Body.Close() |
|
|
|
if getResp.StatusCode != http.StatusOK { |
|
t.Fatalf("Get failed: status %d", getResp.StatusCode) |
|
} |
|
|
|
body, _ := io.ReadAll(getResp.Body) |
|
if !bytes.Equal(body, testData) { |
|
t.Error("Retrieved blob data mismatch") |
|
} |
|
} |
|
|
|
// TestCORSHeaders tests CORS header handling |
|
func TestCORSHeaders(t *testing.T) { |
|
server, cleanup := testSetup(t) |
|
defer cleanup() |
|
|
|
req := httptest.NewRequest("GET", "/test", nil) |
|
w := httptest.NewRecorder() |
|
|
|
server.Handler().ServeHTTP(w, req) |
|
|
|
if w.Header().Get("Access-Control-Allow-Origin") != "*" { |
|
t.Error("Missing CORS header") |
|
} |
|
} |
|
|
|
// TestAuthorizationRequired tests authorization requirement |
|
func TestAuthorizationRequired(t *testing.T) { |
|
server, cleanup := testSetup(t) |
|
defer cleanup() |
|
|
|
// Configure server to require auth |
|
server.requireAuth = true |
|
|
|
testData := []byte("test") |
|
sha256Hash := CalculateSHA256(testData) |
|
pubkey := []byte("testpubkey123456789012345678901234") |
|
|
|
err := server.storage.SaveBlob(sha256Hash, testData, pubkey, "text/plain", "") |
|
if err != nil { |
|
t.Fatalf("Failed to save blob: %v", err) |
|
} |
|
|
|
sha256Hex := hex.Enc(sha256Hash) |
|
|
|
// Request without auth should fail |
|
req := httptest.NewRequest("GET", "/"+sha256Hex, nil) |
|
w := httptest.NewRecorder() |
|
server.Handler().ServeHTTP(w, req) |
|
|
|
if w.Code != http.StatusUnauthorized { |
|
t.Errorf("Expected status 401, got %d", w.Code) |
|
} |
|
} |
|
|
|
// TestACLIntegration tests ACL integration |
|
func TestACLIntegration(t *testing.T) { |
|
server, cleanup := testSetup(t) |
|
defer cleanup() |
|
|
|
// Note: This test assumes ACL is configured |
|
// In a real scenario, you'd set up a proper ACL instance |
|
|
|
_, signer := createTestKeypair(t) |
|
testData := []byte("test") |
|
sha256Hash := CalculateSHA256(testData) |
|
|
|
authEv := createAuthEvent(t, signer, "upload", sha256Hash, 3600) |
|
|
|
req := httptest.NewRequest("PUT", "/upload", bytes.NewReader(testData)) |
|
req.Header.Set("Authorization", createAuthHeader(authEv)) |
|
|
|
w := httptest.NewRecorder() |
|
server.Handler().ServeHTTP(w, req) |
|
|
|
// Should succeed if ACL allows, or fail if not |
|
// The exact behavior depends on ACL configuration |
|
if w.Code != http.StatusOK && w.Code != http.StatusForbidden { |
|
t.Errorf("Unexpected status: %d", w.Code) |
|
} |
|
} |
|
|
|
// TestMimeTypeDetection tests MIME type detection from various sources |
|
func TestMimeTypeDetection(t *testing.T) { |
|
tests := []struct { |
|
contentType string |
|
ext string |
|
expected string |
|
}{ |
|
{"image/png", "", "image/png"}, |
|
{"", ".png", "image/png"}, |
|
{"", ".pdf", "application/pdf"}, |
|
{"application/pdf", ".txt", "application/pdf"}, |
|
{"", ".unknown", "application/octet-stream"}, |
|
{"", "", "application/octet-stream"}, |
|
} |
|
|
|
for _, tt := range tests { |
|
result := DetectMimeType(tt.contentType, tt.ext) |
|
if result != tt.expected { |
|
t.Errorf("DetectMimeType(%q, %q) = %q, want %q", |
|
tt.contentType, tt.ext, result, tt.expected) |
|
} |
|
} |
|
} |
|
|
|
// TestSHA256Validation tests SHA256 validation |
|
func TestSHA256Validation(t *testing.T) { |
|
validHashes := []string{ |
|
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", |
|
"abc123def456789012345678901234567890123456789012345678901234abcd", |
|
} |
|
|
|
invalidHashes := []string{ |
|
"", |
|
"abc", |
|
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855x", |
|
"12345", |
|
} |
|
|
|
for _, hash := range validHashes { |
|
if !ValidateSHA256Hex(hash) { |
|
t.Errorf("Hash %s should be valid", hash) |
|
} |
|
} |
|
|
|
for _, hash := range invalidHashes { |
|
if ValidateSHA256Hex(hash) { |
|
t.Errorf("Hash %s should be invalid", hash) |
|
} |
|
} |
|
} |
|
|
|
// TestBlobURLBuilding tests URL building |
|
func TestBlobURLBuilding(t *testing.T) { |
|
baseURL := "https://example.com" |
|
sha256Hex := "abc123def456" |
|
ext := ".pdf" |
|
|
|
url := BuildBlobURL(baseURL, sha256Hex, ext) |
|
expected := baseURL + sha256Hex + ext |
|
|
|
if url != expected { |
|
t.Errorf("Expected %s, got %s", expected, url) |
|
} |
|
|
|
// Test without extension |
|
url2 := BuildBlobURL(baseURL, sha256Hex, "") |
|
expected2 := baseURL + sha256Hex |
|
|
|
if url2 != expected2 { |
|
t.Errorf("Expected %s, got %s", expected2, url2) |
|
} |
|
} |
|
|
|
// TestErrorResponses tests error response formatting |
|
func TestErrorResponses(t *testing.T) { |
|
server, cleanup := testSetup(t) |
|
defer cleanup() |
|
|
|
w := httptest.NewRecorder() |
|
|
|
server.setErrorResponse(w, http.StatusBadRequest, "Invalid request") |
|
|
|
if w.Code != http.StatusBadRequest { |
|
t.Errorf("Expected status %d, got %d", http.StatusBadRequest, w.Code) |
|
} |
|
|
|
if w.Header().Get("X-Reason") == "" { |
|
t.Error("Missing X-Reason header") |
|
} |
|
} |
|
|
|
// TestExtractSHA256FromURL tests URL hash extraction |
|
func TestExtractSHA256FromURL(t *testing.T) { |
|
tests := []struct { |
|
url string |
|
expected string |
|
hasError bool |
|
}{ |
|
{"https://example.com/abc123def456789012345678901234567890123456789012345678901234abcd", "abc123def456789012345678901234567890123456789012345678901234abcd", false}, |
|
{"https://example.com/user/path/abc123def456789012345678901234567890123456789012345678901234abcd.pdf", "abc123def456789012345678901234567890123456789012345678901234abcd", false}, |
|
{"https://example.com/", "", true}, |
|
{"no hash here", "", true}, |
|
} |
|
|
|
for _, tt := range tests { |
|
hash, err := ExtractSHA256FromURL(tt.url) |
|
if tt.hasError { |
|
if err == nil { |
|
t.Errorf("Expected error for URL %s", tt.url) |
|
} |
|
} else { |
|
if err != nil { |
|
t.Errorf("Unexpected error for URL %s: %v", tt.url, err) |
|
} |
|
if hash != tt.expected { |
|
t.Errorf("Expected %s, got %s for URL %s", tt.expected, hash, tt.url) |
|
} |
|
} |
|
} |
|
} |
|
|
|
// TestStorageReport tests report storage |
|
func TestStorageReport(t *testing.T) { |
|
server, cleanup := testSetup(t) |
|
defer cleanup() |
|
|
|
sha256Hash := CalculateSHA256([]byte("test")) |
|
reportData := []byte("report data") |
|
|
|
err := server.storage.SaveReport(sha256Hash, reportData) |
|
if err != nil { |
|
t.Fatalf("Failed to save report: %v", err) |
|
} |
|
|
|
// Reports are stored but not retrieved in current implementation |
|
// This test verifies the operation doesn't fail |
|
} |
|
|
|
// BenchmarkStorageOperations benchmarks storage operations |
|
func BenchmarkStorageOperations(b *testing.B) { |
|
server, cleanup := testSetup(&testing.T{}) |
|
defer cleanup() |
|
|
|
testData := []byte("benchmark test data") |
|
sha256Hash := CalculateSHA256(testData) |
|
pubkey := []byte("testpubkey123456789012345678901234") |
|
|
|
b.ResetTimer() |
|
for i := 0; i < b.N; i++ { |
|
_ = server.storage.SaveBlob(sha256Hash, testData, pubkey, "text/plain", "") |
|
_, _, _ = server.storage.GetBlob(sha256Hash) |
|
_ = server.storage.DeleteBlob(sha256Hash, pubkey) |
|
} |
|
} |
|
|
|
// TestConcurrentUploads tests concurrent uploads |
|
func TestConcurrentUploads(t *testing.T) { |
|
server, cleanup := testSetup(t) |
|
defer cleanup() |
|
|
|
_, signer := createTestKeypair(t) |
|
|
|
const numUploads = 10 |
|
done := make(chan error, numUploads) |
|
|
|
for i := 0; i < numUploads; i++ { |
|
go func(id int) { |
|
testData := []byte("concurrent test " + string(rune('A'+id))) |
|
sha256Hash := CalculateSHA256(testData) |
|
authEv := createAuthEvent(t, signer, "upload", sha256Hash, 3600) |
|
|
|
req := httptest.NewRequest("PUT", "/upload", bytes.NewReader(testData)) |
|
req.Header.Set("Authorization", createAuthHeader(authEv)) |
|
|
|
w := httptest.NewRecorder() |
|
server.Handler().ServeHTTP(w, req) |
|
|
|
if w.Code != http.StatusOK { |
|
done <- &testError{code: w.Code, body: w.Body.String()} |
|
return |
|
} |
|
done <- nil |
|
}(i) |
|
} |
|
|
|
for i := 0; i < numUploads; i++ { |
|
if err := <-done; err != nil { |
|
t.Errorf("Concurrent upload failed: %v", err) |
|
} |
|
} |
|
} |
|
|
|
type testError struct { |
|
code int |
|
body string |
|
} |
|
|
|
func (e *testError) Error() string { |
|
return strings.Join([]string{"HTTP", string(rune(e.code)), e.body}, " ") |
|
}
|
|
|