diff --git a/Makefile b/Makefile index 150f567..f18aed7 100644 --- a/Makefile +++ b/Makefile @@ -5,9 +5,15 @@ test: @echo Running tests... go test -v $(TEST) +# Gateway-focused E2E tests assume gateway and nodes are already running +# Configure via env: +# GATEWAY_BASE_URL (default http://127.0.0.1:8080) +# GATEWAY_API_KEY (required for auth-protected routes) .PHONY: test-e2e test-e2e: - @echo Running E2E tests... + @echo "Running gateway E2E tests (HTTP/WS only)..." + @echo "Base URL: $${GATEWAY_BASE_URL:-http://127.0.0.1:8080}" + @test -n "$$GATEWAY_API_KEY" || (echo "GATEWAY_API_KEY must be set" && exit 1) go test -v -tags e2e ./e2e # Network - Distributed P2P Database System @@ -37,11 +43,6 @@ clean: rm -rf data/ @echo "Clean complete!" -# Run tests -test: - @echo "Running tests..." - go test -v ./... - # Run bootstrap node (auto-selects identity and data dir) run-node: @echo "Starting bootstrap node with config..." @@ -151,30 +152,6 @@ dev-setup: deps @mkdir -p data/test-bootstrap data/test-node1 data/test-node2 @echo "Development setup complete!" -# Multi-node testing -test-multinode: build - @echo "๐Ÿงช Starting comprehensive multi-node test..." - @chmod +x scripts/test-multinode.sh - @./scripts/test-multinode.sh - -test-peer-discovery: build - @echo "๐Ÿ” Testing peer discovery (requires running nodes)..." - @echo "Connected peers:" - @./bin/network-cli peers --timeout 10s - -test-replication: build - @echo "๐Ÿ”„ Testing data replication (requires running nodes)..." - @./bin/network-cli storage put "replication:test:$$(date +%s)" "Test data - $$(date)" - @sleep 2 - @echo "Retrieving replicated data:" - @./bin/network-cli storage list replication:test: - -test-consensus: build - @echo "๐Ÿ—„๏ธ Testing database consensus (requires running nodes)..." - @./bin/network-cli query "CREATE TABLE IF NOT EXISTS consensus_test (id INTEGER PRIMARY KEY, test_data TEXT, timestamp TEXT)" - @./bin/network-cli query "INSERT INTO consensus_test (test_data, timestamp) VALUES ('Makefile test', '$$(date)')" - @./bin/network-cli query "SELECT * FROM consensus_test ORDER BY id DESC LIMIT 5" - # Start development cluster (requires multiple terminals) dev-cluster: @echo "To start a development cluster, run these commands in separate terminals:" diff --git a/e2e/gateway_e2e_test.go b/e2e/gateway_e2e_test.go index 58d5955..bb3dc2e 100644 --- a/e2e/gateway_e2e_test.go +++ b/e2e/gateway_e2e_test.go @@ -3,11 +3,11 @@ package e2e import ( - "context" - "crypto/rand" + "crypto/rand" "encoding/base64" "encoding/json" "fmt" + "io" "net/http" "net/url" "os" @@ -111,11 +111,10 @@ func TestGateway_Storage_PutGetListExistsDelete(t *testing.T) { if err != nil { t.Fatalf("get do: %v", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { t.Fatalf("get status: %d", resp.StatusCode) } - got := make([]byte, len(payload)) - n, _ := resp.Body.Read(got) - if n == 0 || string(got[:n]) != string(payload[:n]) { - t.Fatalf("payload mismatch: want %q got %q", string(payload), string(got[:n])) - } + got, _ := io.ReadAll(resp.Body) + if string(got) != string(payload) { + t.Fatalf("payload mismatch: want %q got %q", string(payload), string(got)) + } } // LIST (prefix) diff --git a/pkg/gateway/middleware.go b/pkg/gateway/middleware.go index f4f9160..4400395 100644 --- a/pkg/gateway/middleware.go +++ b/pkg/gateway/middleware.go @@ -9,6 +9,7 @@ import ( "strings" "time" + "git.debros.io/DeBros/network/pkg/client" "git.debros.io/DeBros/network/pkg/logging" "git.debros.io/DeBros/network/pkg/storage" "go.uber.org/zap" @@ -95,12 +96,13 @@ func (g *Gateway) authMiddleware(next http.Handler) http.Handler { return } - // Look up API key in DB and derive namespace - db := g.client.Database() - ctx := r.Context() - // Join to namespaces to resolve name in one query - q := "SELECT namespaces.name FROM api_keys JOIN namespaces ON api_keys.namespace_id = namespaces.id WHERE api_keys.key = ? LIMIT 1" - res, err := db.Query(ctx, q, key) + // Look up API key in DB and derive namespace + db := g.client.Database() + // Use internal auth for DB validation (auth not established yet) + internalCtx := client.WithInternalAuth(r.Context()) + // Join to namespaces to resolve name in one query + q := "SELECT namespaces.name FROM api_keys JOIN namespaces ON api_keys.namespace_id = namespaces.id WHERE api_keys.key = ? LIMIT 1" + res, err := db.Query(internalCtx, q, key) if err != nil || res == nil || res.Count == 0 || len(res.Rows) == 0 || len(res.Rows[0]) == 0 { w.Header().Set("WWW-Authenticate", "Bearer error=\"invalid_token\"") writeError(w, http.StatusUnauthorized, "invalid API key") @@ -121,10 +123,10 @@ func (g *Gateway) authMiddleware(next http.Handler) http.Handler { return } - // Attach auth metadata to context for downstream use - ctx = context.WithValue(ctx, ctxKeyAPIKey, key) - ctx = storage.WithNamespace(ctx, ns) - next.ServeHTTP(w, r.WithContext(ctx)) + // Attach auth metadata to context for downstream use + reqCtx := context.WithValue(r.Context(), ctxKeyAPIKey, key) + reqCtx = storage.WithNamespace(reqCtx, ns) + next.ServeHTTP(w, r.WithContext(reqCtx)) }) } @@ -224,22 +226,23 @@ func (g *Gateway) authorizationMiddleware(next http.Handler) http.Handler { return } - // Check ownership in DB - db := g.client.Database() - // Ensure namespace exists and get id - if _, err := db.Query(ctx, "INSERT OR IGNORE INTO namespaces(name) VALUES (?)", ns); err != nil { + // Check ownership in DB using internal auth context + db := g.client.Database() + internalCtx := client.WithInternalAuth(ctx) + // Ensure namespace exists and get id + if _, err := db.Query(internalCtx, "INSERT OR IGNORE INTO namespaces(name) VALUES (?)", ns); err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return } - nres, err := db.Query(ctx, "SELECT id FROM namespaces WHERE name = ? LIMIT 1", ns) + nres, err := db.Query(internalCtx, "SELECT id FROM namespaces WHERE name = ? LIMIT 1", ns) if err != nil || nres == nil || nres.Count == 0 || len(nres.Rows) == 0 || len(nres.Rows[0]) == 0 { writeError(w, http.StatusForbidden, "namespace not found") return } nsID := nres.Rows[0][0] - q := "SELECT 1 FROM namespace_ownership WHERE namespace_id = ? AND owner_type = ? AND owner_id = ? LIMIT 1" - res, err := db.Query(ctx, q, nsID, ownerType, ownerID) + q := "SELECT 1 FROM namespace_ownership WHERE namespace_id = ? AND owner_type = ? AND owner_id = ? LIMIT 1" + res, err := db.Query(internalCtx, q, nsID, ownerType, ownerID) if err != nil || res == nil || res.Count == 0 { writeError(w, http.StatusForbidden, "forbidden: not an owner of namespace") return