refactored all e2e tests

This commit is contained in:
anonpenguin23 2026-01-29 07:50:40 +02:00
parent 81414722cd
commit 29581bec51
25 changed files with 820 additions and 751 deletions

View File

@ -8,7 +8,7 @@ test:
# Gateway-focused E2E tests assume gateway and nodes are already running # Gateway-focused E2E tests assume gateway and nodes are already running
# Auto-discovers configuration from ~/.orama and queries database for API key # Auto-discovers configuration from ~/.orama and queries database for API key
# No environment variables required # No environment variables required
.PHONY: test-e2e test-e2e-deployments test-e2e-fullstack test-e2e-https test-e2e-quick test-e2e-local test-e2e-prod .PHONY: test-e2e test-e2e-deployments test-e2e-fullstack test-e2e-https test-e2e-quick test-e2e-local test-e2e-prod test-e2e-shared test-e2e-cluster test-e2e-integration test-e2e-production
# Check if gateway is running (helper) # Check if gateway is running (helper)
.PHONY: check-gateway .PHONY: check-gateway
@ -32,15 +32,15 @@ test-e2e-local: check-gateway
@echo "Running E2E tests against local dev environment..." @echo "Running E2E tests against local dev environment..."
go test -v -tags e2e -timeout 30m ./e2e/... go test -v -tags e2e -timeout 30m ./e2e/...
# Production E2E tests - requires ORAMA_GATEWAY_URL to be set # Production E2E tests - includes production-only tests
test-e2e-prod: test-e2e-prod:
@if [ -z "$$ORAMA_GATEWAY_URL" ]; then \ @if [ -z "$$ORAMA_GATEWAY_URL" ]; then \
echo "❌ ORAMA_GATEWAY_URL not set"; \ echo "❌ ORAMA_GATEWAY_URL not set"; \
echo "Usage: ORAMA_GATEWAY_URL=http://VPS-IP:6001 make test-e2e-prod"; \ echo "Usage: ORAMA_GATEWAY_URL=http://VPS-IP:6001 make test-e2e-prod"; \
exit 1; \ exit 1; \
fi fi
@echo "Running E2E tests against $$ORAMA_GATEWAY_URL..." @echo "Running E2E tests (including production-only) against $$ORAMA_GATEWAY_URL..."
go test -v -tags e2e -timeout 30m ./e2e/... go test -v -tags "e2e production" -timeout 30m ./e2e/...
# Generic e2e target (works with both local and production) # Generic e2e target (works with both local and production)
test-e2e: test-e2e:
@ -61,6 +61,22 @@ test-e2e-https:
@echo "Running HTTPS/external access E2E tests..." @echo "Running HTTPS/external access E2E tests..."
go test -v -tags e2e -timeout 10m -run "TestHTTPS" ./e2e/... go test -v -tags e2e -timeout 10m -run "TestHTTPS" ./e2e/...
test-e2e-shared:
@echo "Running shared E2E tests..."
go test -v -tags e2e -timeout 10m ./e2e/shared/...
test-e2e-cluster:
@echo "Running cluster E2E tests..."
go test -v -tags e2e -timeout 15m ./e2e/cluster/...
test-e2e-integration:
@echo "Running integration E2E tests..."
go test -v -tags e2e -timeout 20m ./e2e/integration/...
test-e2e-production:
@echo "Running production-only E2E tests..."
go test -v -tags "e2e production" -timeout 15m ./e2e/production/...
test-e2e-quick: test-e2e-quick:
@echo "Running quick E2E smoke tests..." @echo "Running quick E2E smoke tests..."
go test -v -tags e2e -timeout 5m -run "TestStatic|TestHealth" ./e2e/... go test -v -tags e2e -timeout 5m -run "TestStatic|TestHealth" ./e2e/...
@ -156,7 +172,12 @@ help:
@echo "" @echo ""
@echo "E2E Testing:" @echo "E2E Testing:"
@echo " make test-e2e-local - Run E2E tests against local dev (checks gateway first)" @echo " make test-e2e-local - Run E2E tests against local dev (checks gateway first)"
@echo " make test-e2e-prod - Run E2E tests against production (needs ORAMA_GATEWAY_URL)" @echo " make test-e2e-prod - Run all E2E tests incl. production-only (needs ORAMA_GATEWAY_URL)"
@echo " make test-e2e-shared - Run shared E2E tests (cache, storage, pubsub, auth)"
@echo " make test-e2e-cluster - Run cluster E2E tests (libp2p, olric, rqlite, namespace)"
@echo " make test-e2e-integration - Run integration E2E tests (fullstack, persistence, concurrency)"
@echo " make test-e2e-deployments - Run deployment E2E tests"
@echo " make test-e2e-production - Run production-only E2E tests (DNS, HTTPS, cross-node)"
@echo " make test-e2e-quick - Quick smoke tests (static deploys, health checks)" @echo " make test-e2e-quick - Quick smoke tests (static deploys, health checks)"
@echo " make test-e2e - Generic E2E tests (auto-discovers config)" @echo " make test-e2e - Generic E2E tests (auto-discovers config)"
@echo "" @echo ""

View File

@ -1,6 +1,6 @@
//go:build e2e //go:build e2e
package e2e package cluster_test
import ( import (
"bytes" "bytes"
@ -10,6 +10,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/DeBrosOfficial/network/e2e"
"github.com/DeBrosOfficial/network/pkg/ipfs" "github.com/DeBrosOfficial/network/pkg/ipfs"
) )
@ -18,13 +19,13 @@ import (
// For production testing, use storage_http_test.go which uses gateway endpoints. // For production testing, use storage_http_test.go which uses gateway endpoints.
func TestIPFSCluster_Health(t *testing.T) { func TestIPFSCluster_Health(t *testing.T) {
SkipIfProduction(t) // Direct IPFS connection not available in production e2e.SkipIfProduction(t) // Direct IPFS connection not available in production
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel() defer cancel()
logger := NewTestLogger(t) logger := e2e.NewTestLogger(t)
cfg := ipfs.Config{ cfg := ipfs.Config{
ClusterAPIURL: GetIPFSClusterURL(), ClusterAPIURL: e2e.GetIPFSClusterURL(),
Timeout: 10 * time.Second, Timeout: 10 * time.Second,
} }
@ -40,13 +41,13 @@ func TestIPFSCluster_Health(t *testing.T) {
} }
func TestIPFSCluster_GetPeerCount(t *testing.T) { func TestIPFSCluster_GetPeerCount(t *testing.T) {
SkipIfProduction(t) e2e.SkipIfProduction(t)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel() defer cancel()
logger := NewTestLogger(t) logger := e2e.NewTestLogger(t)
cfg := ipfs.Config{ cfg := ipfs.Config{
ClusterAPIURL: GetIPFSClusterURL(), ClusterAPIURL: e2e.GetIPFSClusterURL(),
Timeout: 10 * time.Second, Timeout: 10 * time.Second,
} }
@ -68,13 +69,13 @@ func TestIPFSCluster_GetPeerCount(t *testing.T) {
} }
func TestIPFSCluster_AddFile(t *testing.T) { func TestIPFSCluster_AddFile(t *testing.T) {
SkipIfProduction(t) e2e.SkipIfProduction(t)
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel() defer cancel()
logger := NewTestLogger(t) logger := e2e.NewTestLogger(t)
cfg := ipfs.Config{ cfg := ipfs.Config{
ClusterAPIURL: GetIPFSClusterURL(), ClusterAPIURL: e2e.GetIPFSClusterURL(),
Timeout: 30 * time.Second, Timeout: 30 * time.Second,
} }
@ -101,13 +102,13 @@ func TestIPFSCluster_AddFile(t *testing.T) {
} }
func TestIPFSCluster_PinFile(t *testing.T) { func TestIPFSCluster_PinFile(t *testing.T) {
SkipIfProduction(t) e2e.SkipIfProduction(t)
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel() defer cancel()
logger := NewTestLogger(t) logger := e2e.NewTestLogger(t)
cfg := ipfs.Config{ cfg := ipfs.Config{
ClusterAPIURL: GetIPFSClusterURL(), ClusterAPIURL: e2e.GetIPFSClusterURL(),
Timeout: 30 * time.Second, Timeout: 30 * time.Second,
} }
@ -139,13 +140,13 @@ func TestIPFSCluster_PinFile(t *testing.T) {
} }
func TestIPFSCluster_PinStatus(t *testing.T) { func TestIPFSCluster_PinStatus(t *testing.T) {
SkipIfProduction(t) e2e.SkipIfProduction(t)
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel() defer cancel()
logger := NewTestLogger(t) logger := e2e.NewTestLogger(t)
cfg := ipfs.Config{ cfg := ipfs.Config{
ClusterAPIURL: GetIPFSClusterURL(), ClusterAPIURL: e2e.GetIPFSClusterURL(),
Timeout: 30 * time.Second, Timeout: 30 * time.Second,
} }
@ -173,7 +174,7 @@ func TestIPFSCluster_PinStatus(t *testing.T) {
} }
// Give pin time to propagate // Give pin time to propagate
Delay(1000) e2e.Delay(1000)
// Get status // Get status
status, err := client.PinStatus(ctx, cid) status, err := client.PinStatus(ctx, cid)
@ -197,13 +198,13 @@ func TestIPFSCluster_PinStatus(t *testing.T) {
} }
func TestIPFSCluster_UnpinFile(t *testing.T) { func TestIPFSCluster_UnpinFile(t *testing.T) {
SkipIfProduction(t) e2e.SkipIfProduction(t)
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel() defer cancel()
logger := NewTestLogger(t) logger := e2e.NewTestLogger(t)
cfg := ipfs.Config{ cfg := ipfs.Config{
ClusterAPIURL: GetIPFSClusterURL(), ClusterAPIURL: e2e.GetIPFSClusterURL(),
Timeout: 30 * time.Second, Timeout: 30 * time.Second,
} }
@ -236,13 +237,13 @@ func TestIPFSCluster_UnpinFile(t *testing.T) {
} }
func TestIPFSCluster_GetFile(t *testing.T) { func TestIPFSCluster_GetFile(t *testing.T) {
SkipIfProduction(t) e2e.SkipIfProduction(t)
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel() defer cancel()
logger := NewTestLogger(t) logger := e2e.NewTestLogger(t)
cfg := ipfs.Config{ cfg := ipfs.Config{
ClusterAPIURL: GetIPFSClusterURL(), ClusterAPIURL: e2e.GetIPFSClusterURL(),
Timeout: 30 * time.Second, Timeout: 30 * time.Second,
} }
@ -261,10 +262,10 @@ func TestIPFSCluster_GetFile(t *testing.T) {
cid := addResult.Cid cid := addResult.Cid
// Give time for propagation // Give time for propagation
Delay(1000) e2e.Delay(1000)
// Get file // Get file
rc, err := client.Get(ctx, cid, GetIPFSAPIURL()) rc, err := client.Get(ctx, cid, e2e.GetIPFSAPIURL())
if err != nil { if err != nil {
t.Fatalf("get file failed: %v", err) t.Fatalf("get file failed: %v", err)
} }
@ -283,13 +284,13 @@ func TestIPFSCluster_GetFile(t *testing.T) {
} }
func TestIPFSCluster_LargeFile(t *testing.T) { func TestIPFSCluster_LargeFile(t *testing.T) {
SkipIfProduction(t) e2e.SkipIfProduction(t)
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel() defer cancel()
logger := NewTestLogger(t) logger := e2e.NewTestLogger(t)
cfg := ipfs.Config{ cfg := ipfs.Config{
ClusterAPIURL: GetIPFSClusterURL(), ClusterAPIURL: e2e.GetIPFSClusterURL(),
Timeout: 60 * time.Second, Timeout: 60 * time.Second,
} }
@ -317,13 +318,13 @@ func TestIPFSCluster_LargeFile(t *testing.T) {
} }
func TestIPFSCluster_ReplicationFactor(t *testing.T) { func TestIPFSCluster_ReplicationFactor(t *testing.T) {
SkipIfProduction(t) // Direct IPFS connection not available in production e2e.SkipIfProduction(t) // Direct IPFS connection not available in production
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel() defer cancel()
logger := NewTestLogger(t) logger := e2e.NewTestLogger(t)
cfg := ipfs.Config{ cfg := ipfs.Config{
ClusterAPIURL: GetIPFSClusterURL(), ClusterAPIURL: e2e.GetIPFSClusterURL(),
Timeout: 30 * time.Second, Timeout: 30 * time.Second,
} }
@ -353,7 +354,7 @@ func TestIPFSCluster_ReplicationFactor(t *testing.T) {
} }
// Give time for replication // Give time for replication
Delay(2000) e2e.Delay(2000)
// Check status // Check status
status, err := client.PinStatus(ctx, cid) status, err := client.PinStatus(ctx, cid)
@ -365,13 +366,13 @@ func TestIPFSCluster_ReplicationFactor(t *testing.T) {
} }
func TestIPFSCluster_MultipleFiles(t *testing.T) { func TestIPFSCluster_MultipleFiles(t *testing.T) {
SkipIfProduction(t) // Direct IPFS connection not available in production e2e.SkipIfProduction(t) // Direct IPFS connection not available in production
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel() defer cancel()
logger := NewTestLogger(t) logger := e2e.NewTestLogger(t)
cfg := ipfs.Config{ cfg := ipfs.Config{
ClusterAPIURL: GetIPFSClusterURL(), ClusterAPIURL: e2e.GetIPFSClusterURL(),
Timeout: 30 * time.Second, Timeout: 30 * time.Second,
} }

View File

@ -1,6 +1,6 @@
//go:build e2e //go:build e2e
package e2e package cluster_test
import ( import (
"context" "context"
@ -8,25 +8,27 @@ import (
"strings" "strings"
"testing" "testing"
"time" "time"
"github.com/DeBrosOfficial/network/e2e"
) )
func TestLibP2P_PeerConnectivity(t *testing.T) { func TestLibP2P_PeerConnectivity(t *testing.T) {
SkipIfMissingGateway(t) e2e.SkipIfMissingGateway(t)
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel() defer cancel()
// Create and connect client // Create and connect client
c := NewNetworkClient(t) c := e2e.NewNetworkClient(t)
if err := c.Connect(); err != nil { if err := c.Connect(); err != nil {
t.Fatalf("connect failed: %v", err) t.Fatalf("connect failed: %v", err)
} }
defer c.Disconnect() defer c.Disconnect()
// Verify peer connectivity through the gateway // Verify peer connectivity through the gateway
req := &HTTPRequest{ req := &e2e.HTTPRequest{
Method: http.MethodGet, Method: http.MethodGet,
URL: GetGatewayURL() + "/v1/network/peers", URL: e2e.GetGatewayURL() + "/v1/network/peers",
} }
body, status, err := req.Do(ctx) body, status, err := req.Do(ctx)
@ -39,7 +41,7 @@ func TestLibP2P_PeerConnectivity(t *testing.T) {
} }
var resp map[string]interface{} var resp map[string]interface{}
if err := DecodeJSON(body, &resp); err != nil { if err := e2e.DecodeJSON(body, &resp); err != nil {
t.Fatalf("failed to decode response: %v", err) t.Fatalf("failed to decode response: %v", err)
} }
@ -50,30 +52,30 @@ func TestLibP2P_PeerConnectivity(t *testing.T) {
} }
func TestLibP2P_BootstrapPeers(t *testing.T) { func TestLibP2P_BootstrapPeers(t *testing.T) {
SkipIfMissingGateway(t) e2e.SkipIfMissingGateway(t)
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel() defer cancel()
bootstrapPeers := GetBootstrapPeers() bootstrapPeers := e2e.GetBootstrapPeers()
if len(bootstrapPeers) == 0 { if len(bootstrapPeers) == 0 {
t.Skipf("E2E_BOOTSTRAP_PEERS not set; skipping") t.Skipf("E2E_BOOTSTRAP_PEERS not set; skipping")
} }
// Create client with bootstrap peers explicitly set // Create client with bootstrap peers explicitly set
c := NewNetworkClient(t) c := e2e.NewNetworkClient(t)
if err := c.Connect(); err != nil { if err := c.Connect(); err != nil {
t.Fatalf("connect failed: %v", err) t.Fatalf("connect failed: %v", err)
} }
defer c.Disconnect() defer c.Disconnect()
// Give peer discovery time // Give peer discovery time
Delay(2000) e2e.Delay(2000)
// Verify we're connected (check via gateway status) // Verify we're connected (check via gateway status)
req := &HTTPRequest{ req := &e2e.HTTPRequest{
Method: http.MethodGet, Method: http.MethodGet,
URL: GetGatewayURL() + "/v1/network/status", URL: e2e.GetGatewayURL() + "/v1/network/status",
} }
body, status, err := req.Do(ctx) body, status, err := req.Do(ctx)
@ -86,7 +88,7 @@ func TestLibP2P_BootstrapPeers(t *testing.T) {
} }
var resp map[string]interface{} var resp map[string]interface{}
if err := DecodeJSON(body, &resp); err != nil { if err := e2e.DecodeJSON(body, &resp); err != nil {
t.Fatalf("failed to decode response: %v", err) t.Fatalf("failed to decode response: %v", err)
} }
@ -96,15 +98,15 @@ func TestLibP2P_BootstrapPeers(t *testing.T) {
} }
func TestLibP2P_MultipleClientConnections(t *testing.T) { func TestLibP2P_MultipleClientConnections(t *testing.T) {
SkipIfMissingGateway(t) e2e.SkipIfMissingGateway(t)
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel() defer cancel()
// Create multiple clients // Create multiple clients
c1 := NewNetworkClient(t) c1 := e2e.NewNetworkClient(t)
c2 := NewNetworkClient(t) c2 := e2e.NewNetworkClient(t)
c3 := NewNetworkClient(t) c3 := e2e.NewNetworkClient(t)
if err := c1.Connect(); err != nil { if err := c1.Connect(); err != nil {
t.Fatalf("c1 connect failed: %v", err) t.Fatalf("c1 connect failed: %v", err)
@ -122,12 +124,12 @@ func TestLibP2P_MultipleClientConnections(t *testing.T) {
defer c3.Disconnect() defer c3.Disconnect()
// Give peer discovery time // Give peer discovery time
Delay(2000) e2e.Delay(2000)
// Verify gateway sees multiple peers // Verify gateway sees multiple peers
req := &HTTPRequest{ req := &e2e.HTTPRequest{
Method: http.MethodGet, Method: http.MethodGet,
URL: GetGatewayURL() + "/v1/network/peers", URL: e2e.GetGatewayURL() + "/v1/network/peers",
} }
body, status, err := req.Do(ctx) body, status, err := req.Do(ctx)
@ -140,7 +142,7 @@ func TestLibP2P_MultipleClientConnections(t *testing.T) {
} }
var resp map[string]interface{} var resp map[string]interface{}
if err := DecodeJSON(body, &resp); err != nil { if err := e2e.DecodeJSON(body, &resp); err != nil {
t.Fatalf("failed to decode response: %v", err) t.Fatalf("failed to decode response: %v", err)
} }
@ -151,12 +153,12 @@ func TestLibP2P_MultipleClientConnections(t *testing.T) {
} }
func TestLibP2P_ReconnectAfterDisconnect(t *testing.T) { func TestLibP2P_ReconnectAfterDisconnect(t *testing.T) {
SkipIfMissingGateway(t) e2e.SkipIfMissingGateway(t)
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel() defer cancel()
c := NewNetworkClient(t) c := e2e.NewNetworkClient(t)
// Connect // Connect
if err := c.Connect(); err != nil { if err := c.Connect(); err != nil {
@ -164,9 +166,9 @@ func TestLibP2P_ReconnectAfterDisconnect(t *testing.T) {
} }
// Verify connected via gateway // Verify connected via gateway
req1 := &HTTPRequest{ req1 := &e2e.HTTPRequest{
Method: http.MethodGet, Method: http.MethodGet,
URL: GetGatewayURL() + "/v1/network/status", URL: e2e.GetGatewayURL() + "/v1/network/status",
} }
_, status1, err := req1.Do(ctx) _, status1, err := req1.Do(ctx)
@ -180,7 +182,7 @@ func TestLibP2P_ReconnectAfterDisconnect(t *testing.T) {
} }
// Give time for disconnect to propagate // Give time for disconnect to propagate
Delay(500) e2e.Delay(500)
// Reconnect // Reconnect
if err := c.Connect(); err != nil { if err := c.Connect(); err != nil {
@ -189,9 +191,9 @@ func TestLibP2P_ReconnectAfterDisconnect(t *testing.T) {
defer c.Disconnect() defer c.Disconnect()
// Verify connected via gateway again // Verify connected via gateway again
req2 := &HTTPRequest{ req2 := &e2e.HTTPRequest{
Method: http.MethodGet, Method: http.MethodGet,
URL: GetGatewayURL() + "/v1/network/status", URL: e2e.GetGatewayURL() + "/v1/network/status",
} }
_, status2, err := req2.Do(ctx) _, status2, err := req2.Do(ctx)
@ -201,25 +203,25 @@ func TestLibP2P_ReconnectAfterDisconnect(t *testing.T) {
} }
func TestLibP2P_PeerDiscovery(t *testing.T) { func TestLibP2P_PeerDiscovery(t *testing.T) {
SkipIfMissingGateway(t) e2e.SkipIfMissingGateway(t)
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel() defer cancel()
// Create client // Create client
c := NewNetworkClient(t) c := e2e.NewNetworkClient(t)
if err := c.Connect(); err != nil { if err := c.Connect(); err != nil {
t.Fatalf("connect failed: %v", err) t.Fatalf("connect failed: %v", err)
} }
defer c.Disconnect() defer c.Disconnect()
// Give peer discovery time // Give peer discovery time
Delay(3000) e2e.Delay(3000)
// Get peer list // Get peer list
req := &HTTPRequest{ req := &e2e.HTTPRequest{
Method: http.MethodGet, Method: http.MethodGet,
URL: GetGatewayURL() + "/v1/network/peers", URL: e2e.GetGatewayURL() + "/v1/network/peers",
} }
body, status, err := req.Do(ctx) body, status, err := req.Do(ctx)
@ -232,7 +234,7 @@ func TestLibP2P_PeerDiscovery(t *testing.T) {
} }
var resp map[string]interface{} var resp map[string]interface{}
if err := DecodeJSON(body, &resp); err != nil { if err := e2e.DecodeJSON(body, &resp); err != nil {
t.Fatalf("failed to decode response: %v", err) t.Fatalf("failed to decode response: %v", err)
} }
@ -251,22 +253,22 @@ func TestLibP2P_PeerDiscovery(t *testing.T) {
} }
func TestLibP2P_PeerAddressFormat(t *testing.T) { func TestLibP2P_PeerAddressFormat(t *testing.T) {
SkipIfMissingGateway(t) e2e.SkipIfMissingGateway(t)
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel() defer cancel()
// Create client // Create client
c := NewNetworkClient(t) c := e2e.NewNetworkClient(t)
if err := c.Connect(); err != nil { if err := c.Connect(); err != nil {
t.Fatalf("connect failed: %v", err) t.Fatalf("connect failed: %v", err)
} }
defer c.Disconnect() defer c.Disconnect()
// Get peer list // Get peer list
req := &HTTPRequest{ req := &e2e.HTTPRequest{
Method: http.MethodGet, Method: http.MethodGet,
URL: GetGatewayURL() + "/v1/network/peers", URL: e2e.GetGatewayURL() + "/v1/network/peers",
} }
body, status, err := req.Do(ctx) body, status, err := req.Do(ctx)
@ -279,7 +281,7 @@ func TestLibP2P_PeerAddressFormat(t *testing.T) {
} }
var resp map[string]interface{} var resp map[string]interface{}
if err := DecodeJSON(body, &resp); err != nil { if err := e2e.DecodeJSON(body, &resp); err != nil {
t.Fatalf("failed to decode response: %v", err) t.Fatalf("failed to decode response: %v", err)
} }

View File

@ -1,6 +1,6 @@
//go:build e2e //go:build e2e
package e2e package cluster_test
import ( import (
"context" "context"
@ -16,6 +16,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/DeBrosOfficial/network/e2e"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -30,7 +31,7 @@ func TestNamespaceCluster_FullProvisioning(t *testing.T) {
// Generate unique namespace name // Generate unique namespace name
newNamespace := fmt.Sprintf("e2e-cluster-%d", time.Now().UnixNano()) newNamespace := fmt.Sprintf("e2e-cluster-%d", time.Now().UnixNano())
env, err := LoadTestEnvWithNamespace(newNamespace) env, err := e2e.LoadTestEnvWithNamespace(newNamespace)
require.NoError(t, err, "FATAL: Failed to create test environment for namespace %s", newNamespace) require.NoError(t, err, "FATAL: Failed to create test environment for namespace %s", newNamespace)
require.NotEmpty(t, env.APIKey, "FATAL: No API key received - namespace provisioning failed") require.NotEmpty(t, env.APIKey, "FATAL: No API key received - namespace provisioning failed")
@ -70,7 +71,7 @@ func TestNamespaceCluster_FullProvisioning(t *testing.T) {
} }
deploymentName := fmt.Sprintf("cluster-test-%d", time.Now().Unix()) deploymentName := fmt.Sprintf("cluster-test-%d", time.Now().Unix())
deploymentID := CreateTestDeployment(t, env, deploymentName, tarballPath) deploymentID := e2e.CreateTestDeployment(t, env, deploymentName, tarballPath)
require.NotEmpty(t, deploymentID, "FAIL: Deployment creation failed on namespace cluster") require.NotEmpty(t, deploymentID, "FAIL: Deployment creation failed on namespace cluster")
t.Logf("Created deployment %s (ID: %s) on namespace %s", deploymentName, deploymentID, newNamespace) t.Logf("Created deployment %s (ID: %s) on namespace %s", deploymentName, deploymentID, newNamespace)
@ -78,7 +79,7 @@ func TestNamespaceCluster_FullProvisioning(t *testing.T) {
// Cleanup // Cleanup
defer func() { defer func() {
if !env.SkipCleanup { if !env.SkipCleanup {
DeleteDeployment(t, env, deploymentID) e2e.DeleteDeployment(t, env, deploymentID)
} }
}() }()
@ -246,7 +247,7 @@ func TestNamespaceCluster_ProvisioningCreatesProcesses(t *testing.T) {
t.Logf("Ports in use before provisioning: %v", portsBefore) t.Logf("Ports in use before provisioning: %v", portsBefore)
// Create namespace // Create namespace
env, err := LoadTestEnvWithNamespace(newNamespace) env, err := e2e.LoadTestEnvWithNamespace(newNamespace)
require.NoError(t, err, "FATAL: Failed to create namespace") require.NoError(t, err, "FATAL: Failed to create namespace")
require.NotEmpty(t, env.APIKey, "FATAL: No API key - provisioning failed") require.NotEmpty(t, env.APIKey, "FATAL: No API key - provisioning failed")
@ -330,7 +331,7 @@ func TestNamespaceCluster_ProvisioningCreatesProcesses(t *testing.T) {
// TestNamespaceCluster_StatusEndpoint tests the /v1/namespace/status endpoint // TestNamespaceCluster_StatusEndpoint tests the /v1/namespace/status endpoint
func TestNamespaceCluster_StatusEndpoint(t *testing.T) { func TestNamespaceCluster_StatusEndpoint(t *testing.T) {
env, err := LoadTestEnv() env, err := e2e.LoadTestEnv()
require.NoError(t, err, "Failed to load test environment") require.NoError(t, err, "Failed to load test environment")
t.Run("Status endpoint returns 404 for non-existent cluster", func(t *testing.T) { t.Run("Status endpoint returns 404 for non-existent cluster", func(t *testing.T) {
@ -351,10 +352,10 @@ func TestNamespaceCluster_CrossNamespaceAccess(t *testing.T) {
nsA := fmt.Sprintf("ns-a-%d", time.Now().Unix()) nsA := fmt.Sprintf("ns-a-%d", time.Now().Unix())
nsB := fmt.Sprintf("ns-b-%d", time.Now().Unix()) nsB := fmt.Sprintf("ns-b-%d", time.Now().Unix())
envA, err := LoadTestEnvWithNamespace(nsA) envA, err := e2e.LoadTestEnvWithNamespace(nsA)
require.NoError(t, err, "FAIL: Cannot create namespace A") require.NoError(t, err, "FAIL: Cannot create namespace A")
envB, err := LoadTestEnvWithNamespace(nsB) envB, err := e2e.LoadTestEnvWithNamespace(nsB)
require.NoError(t, err, "FAIL: Cannot create namespace B") require.NoError(t, err, "FAIL: Cannot create namespace B")
// Verify both namespaces have different API keys // Verify both namespaces have different API keys
@ -392,7 +393,7 @@ func TestNamespaceCluster_CrossNamespaceAccess(t *testing.T) {
// TestDeployment_SubdomainFormat tests deployment subdomain format // TestDeployment_SubdomainFormat tests deployment subdomain format
func TestDeployment_SubdomainFormat(t *testing.T) { func TestDeployment_SubdomainFormat(t *testing.T) {
env, err := LoadTestEnv() env, err := e2e.LoadTestEnv()
require.NoError(t, err, "Failed to load test environment") require.NoError(t, err, "Failed to load test environment")
tarballPath := filepath.Join("../testdata/tarballs/react-vite.tar.gz") tarballPath := filepath.Join("../testdata/tarballs/react-vite.tar.gz")
@ -401,12 +402,12 @@ func TestDeployment_SubdomainFormat(t *testing.T) {
} }
deploymentName := fmt.Sprintf("subdomain-test-%d", time.Now().UnixNano()) deploymentName := fmt.Sprintf("subdomain-test-%d", time.Now().UnixNano())
deploymentID := CreateTestDeployment(t, env, deploymentName, tarballPath) deploymentID := e2e.CreateTestDeployment(t, env, deploymentName, tarballPath)
require.NotEmpty(t, deploymentID, "FAIL: Deployment creation failed") require.NotEmpty(t, deploymentID, "FAIL: Deployment creation failed")
defer func() { defer func() {
if !env.SkipCleanup { if !env.SkipCleanup {
DeleteDeployment(t, env, deploymentID) e2e.DeleteDeployment(t, env, deploymentID)
} }
}() }()

View File

@ -1,6 +1,6 @@
//go:build e2e //go:build e2e
package e2e package cluster_test
import ( import (
"bytes" "bytes"
@ -12,35 +12,36 @@ import (
"testing" "testing"
"time" "time"
"github.com/DeBrosOfficial/network/e2e"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func TestNamespaceIsolation_Deployments(t *testing.T) { func TestNamespaceIsolation_Deployments(t *testing.T) {
// Setup two test environments with different namespaces // Setup two test environments with different namespaces
envA, err := LoadTestEnvWithNamespace("namespace-a-" + fmt.Sprintf("%d", time.Now().Unix())) envA, err := e2e.LoadTestEnvWithNamespace("namespace-a-" + fmt.Sprintf("%d", time.Now().Unix()))
require.NoError(t, err, "Failed to create namespace A environment") require.NoError(t, err, "Failed to create namespace A environment")
envB, err := LoadTestEnvWithNamespace("namespace-b-" + fmt.Sprintf("%d", time.Now().Unix())) envB, err := e2e.LoadTestEnvWithNamespace("namespace-b-" + fmt.Sprintf("%d", time.Now().Unix()))
require.NoError(t, err, "Failed to create namespace B environment") require.NoError(t, err, "Failed to create namespace B environment")
tarballPath := filepath.Join("../testdata/tarballs/react-vite.tar.gz") tarballPath := filepath.Join("../testdata/tarballs/react-vite.tar.gz")
// Create deployment in namespace-a // Create deployment in namespace-a
deploymentNameA := "test-app-ns-a" deploymentNameA := "test-app-ns-a"
deploymentIDA := CreateTestDeployment(t, envA, deploymentNameA, tarballPath) deploymentIDA := e2e.CreateTestDeployment(t, envA, deploymentNameA, tarballPath)
defer func() { defer func() {
if !envA.SkipCleanup { if !envA.SkipCleanup {
DeleteDeployment(t, envA, deploymentIDA) e2e.DeleteDeployment(t, envA, deploymentIDA)
} }
}() }()
// Create deployment in namespace-b // Create deployment in namespace-b
deploymentNameB := "test-app-ns-b" deploymentNameB := "test-app-ns-b"
deploymentIDB := CreateTestDeployment(t, envB, deploymentNameB, tarballPath) deploymentIDB := e2e.CreateTestDeployment(t, envB, deploymentNameB, tarballPath)
defer func() { defer func() {
if !envB.SkipCleanup { if !envB.SkipCleanup {
DeleteDeployment(t, envB, deploymentIDB) e2e.DeleteDeployment(t, envB, deploymentIDB)
} }
}() }()
@ -112,27 +113,27 @@ func TestNamespaceIsolation_Deployments(t *testing.T) {
} }
func TestNamespaceIsolation_SQLiteDatabases(t *testing.T) { func TestNamespaceIsolation_SQLiteDatabases(t *testing.T) {
envA, err := LoadTestEnvWithNamespace("namespace-a-" + fmt.Sprintf("%d", time.Now().Unix())) envA, err := e2e.LoadTestEnvWithNamespace("namespace-a-" + fmt.Sprintf("%d", time.Now().Unix()))
require.NoError(t, err, "Should create test environment for namespace-a") require.NoError(t, err, "Should create test environment for namespace-a")
envB, err := LoadTestEnvWithNamespace("namespace-b-" + fmt.Sprintf("%d", time.Now().Unix())) envB, err := e2e.LoadTestEnvWithNamespace("namespace-b-" + fmt.Sprintf("%d", time.Now().Unix()))
require.NoError(t, err, "Should create test environment for namespace-b") require.NoError(t, err, "Should create test environment for namespace-b")
// Create database in namespace-a // Create database in namespace-a
dbNameA := "users-db-a" dbNameA := "users-db-a"
CreateSQLiteDB(t, envA, dbNameA) e2e.CreateSQLiteDB(t, envA, dbNameA)
defer func() { defer func() {
if !envA.SkipCleanup { if !envA.SkipCleanup {
DeleteSQLiteDB(t, envA, dbNameA) e2e.DeleteSQLiteDB(t, envA, dbNameA)
} }
}() }()
// Create database in namespace-b // Create database in namespace-b
dbNameB := "users-db-b" dbNameB := "users-db-b"
CreateSQLiteDB(t, envB, dbNameB) e2e.CreateSQLiteDB(t, envB, dbNameB)
defer func() { defer func() {
if !envB.SkipCleanup { if !envB.SkipCleanup {
DeleteSQLiteDB(t, envB, dbNameB) e2e.DeleteSQLiteDB(t, envB, dbNameB)
} }
}() }()
@ -201,17 +202,17 @@ func TestNamespaceIsolation_SQLiteDatabases(t *testing.T) {
} }
func TestNamespaceIsolation_IPFSContent(t *testing.T) { func TestNamespaceIsolation_IPFSContent(t *testing.T) {
envA, err := LoadTestEnvWithNamespace("namespace-a-" + fmt.Sprintf("%d", time.Now().Unix())) envA, err := e2e.LoadTestEnvWithNamespace("namespace-a-" + fmt.Sprintf("%d", time.Now().Unix()))
require.NoError(t, err, "Should create test environment for namespace-a") require.NoError(t, err, "Should create test environment for namespace-a")
envB, err := LoadTestEnvWithNamespace("namespace-b-" + fmt.Sprintf("%d", time.Now().Unix())) envB, err := e2e.LoadTestEnvWithNamespace("namespace-b-" + fmt.Sprintf("%d", time.Now().Unix()))
require.NoError(t, err, "Should create test environment for namespace-b") require.NoError(t, err, "Should create test environment for namespace-b")
// Upload file in namespace-a // Upload file in namespace-a
cidA := UploadTestFile(t, envA, "test-file-a.txt", "Content from namespace A") cidA := e2e.UploadTestFile(t, envA, "test-file-a.txt", "Content from namespace A")
defer func() { defer func() {
if !envA.SkipCleanup { if !envA.SkipCleanup {
UnpinFile(t, envA, cidA) e2e.UnpinFile(t, envA, cidA)
} }
}() }()
@ -273,10 +274,10 @@ func TestNamespaceIsolation_IPFSContent(t *testing.T) {
} }
func TestNamespaceIsolation_OlricCache(t *testing.T) { func TestNamespaceIsolation_OlricCache(t *testing.T) {
envA, err := LoadTestEnvWithNamespace("namespace-a-" + fmt.Sprintf("%d", time.Now().Unix())) envA, err := e2e.LoadTestEnvWithNamespace("namespace-a-" + fmt.Sprintf("%d", time.Now().Unix()))
require.NoError(t, err, "Should create test environment for namespace-a") require.NoError(t, err, "Should create test environment for namespace-a")
envB, err := LoadTestEnvWithNamespace("namespace-b-" + fmt.Sprintf("%d", time.Now().Unix())) envB, err := e2e.LoadTestEnvWithNamespace("namespace-b-" + fmt.Sprintf("%d", time.Now().Unix()))
require.NoError(t, err, "Should create test environment for namespace-b") require.NoError(t, err, "Should create test environment for namespace-b")
dmap := "test-cache" dmap := "test-cache"

View File

@ -1,17 +1,17 @@
//go:build e2e //go:build e2e
package e2e package cluster_test
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io"
"net" "net"
"net/http" "net/http"
"strings" "strings"
"testing" "testing"
"time" "time"
"github.com/DeBrosOfficial/network/e2e"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -31,86 +31,10 @@ func getOlricNodeAddresses() []string {
} }
} }
// putToOlric stores a key-value pair in Olric via HTTP API
func putToOlric(gatewayURL, apiKey, dmap, key, value string) error {
reqBody := map[string]interface{}{
"dmap": dmap,
"key": key,
"value": value,
}
bodyBytes, _ := json.Marshal(reqBody)
req, err := http.NewRequest("POST", gatewayURL+"/v1/cache/put", strings.NewReader(string(bodyBytes)))
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+apiKey)
client := &http.Client{Timeout: 10 * time.Second}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated {
body, _ := io.ReadAll(resp.Body)
return fmt.Errorf("put failed with status %d: %s", resp.StatusCode, string(body))
}
return nil
}
// getFromOlric retrieves a value from Olric via HTTP API
func getFromOlric(gatewayURL, apiKey, dmap, key string) (string, error) {
reqBody := map[string]interface{}{
"dmap": dmap,
"key": key,
}
bodyBytes, _ := json.Marshal(reqBody)
req, err := http.NewRequest("POST", gatewayURL+"/v1/cache/get", strings.NewReader(string(bodyBytes)))
if err != nil {
return "", err
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+apiKey)
client := &http.Client{Timeout: 10 * time.Second}
resp, err := client.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
if resp.StatusCode == http.StatusNotFound {
return "", fmt.Errorf("key not found")
}
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return "", fmt.Errorf("get failed with status %d: %s", resp.StatusCode, string(body))
}
body, _ := io.ReadAll(resp.Body)
var result map[string]interface{}
if err := json.Unmarshal(body, &result); err != nil {
return "", err
}
if value, ok := result["value"].(string); ok {
return value, nil
}
// Value might be in a different format
if value, ok := result["value"]; ok {
return fmt.Sprintf("%v", value), nil
}
return "", fmt.Errorf("value not found in response")
}
// TestOlric_BasicDistribution verifies cache operations work across the cluster. // TestOlric_BasicDistribution verifies cache operations work across the cluster.
func TestOlric_BasicDistribution(t *testing.T) { func TestOlric_BasicDistribution(t *testing.T) {
// Note: Not using SkipIfMissingGateway() since LoadTestEnv() creates its own API key // Note: Not using SkipIfMissingGateway() since LoadTestEnv() creates its own API key
env, err := LoadTestEnv() env, err := e2e.LoadTestEnv()
require.NoError(t, err, "FAIL: Could not load test environment") require.NoError(t, err, "FAIL: Could not load test environment")
require.NotEmpty(t, env.APIKey, "FAIL: No API key available") require.NotEmpty(t, env.APIKey, "FAIL: No API key available")
@ -121,11 +45,11 @@ func TestOlric_BasicDistribution(t *testing.T) {
value := fmt.Sprintf("value_%d", time.Now().UnixNano()) value := fmt.Sprintf("value_%d", time.Now().UnixNano())
// Put // Put
err := putToOlric(env.GatewayURL, env.APIKey, dmap, key, value) err := e2e.PutToOlric(env.GatewayURL, env.APIKey, dmap, key, value)
require.NoError(t, err, "FAIL: Could not put value to cache") require.NoError(t, err, "FAIL: Could not put value to cache")
// Get // Get
retrieved, err := getFromOlric(env.GatewayURL, env.APIKey, dmap, key) retrieved, err := e2e.GetFromOlric(env.GatewayURL, env.APIKey, dmap, key)
require.NoError(t, err, "FAIL: Could not get value from cache") require.NoError(t, err, "FAIL: Could not get value from cache")
require.Equal(t, value, retrieved, "FAIL: Retrieved value doesn't match") require.Equal(t, value, retrieved, "FAIL: Retrieved value doesn't match")
@ -140,7 +64,7 @@ func TestOlric_BasicDistribution(t *testing.T) {
value := fmt.Sprintf("dist_value_%d", i) value := fmt.Sprintf("dist_value_%d", i)
keys[key] = value keys[key] = value
err := putToOlric(env.GatewayURL, env.APIKey, dmap, key, value) err := e2e.PutToOlric(env.GatewayURL, env.APIKey, dmap, key, value)
require.NoError(t, err, "FAIL: Could not put key %s", key) require.NoError(t, err, "FAIL: Could not put key %s", key)
} }
@ -148,7 +72,7 @@ func TestOlric_BasicDistribution(t *testing.T) {
// Verify all keys are retrievable // Verify all keys are retrievable
for key, expectedValue := range keys { for key, expectedValue := range keys {
retrieved, err := getFromOlric(env.GatewayURL, env.APIKey, dmap, key) retrieved, err := e2e.GetFromOlric(env.GatewayURL, env.APIKey, dmap, key)
require.NoError(t, err, "FAIL: Could not get key %s", key) require.NoError(t, err, "FAIL: Could not get key %s", key)
require.Equal(t, expectedValue, retrieved, "FAIL: Value mismatch for key %s", key) require.Equal(t, expectedValue, retrieved, "FAIL: Value mismatch for key %s", key)
} }
@ -159,7 +83,7 @@ func TestOlric_BasicDistribution(t *testing.T) {
// TestOlric_ConcurrentAccess verifies cache handles concurrent operations correctly. // TestOlric_ConcurrentAccess verifies cache handles concurrent operations correctly.
func TestOlric_ConcurrentAccess(t *testing.T) { func TestOlric_ConcurrentAccess(t *testing.T) {
env, err := LoadTestEnv() env, err := e2e.LoadTestEnv()
require.NoError(t, err, "FAIL: Could not load test environment") require.NoError(t, err, "FAIL: Could not load test environment")
dmap := fmt.Sprintf("concurrent_test_%d", time.Now().UnixNano()) dmap := fmt.Sprintf("concurrent_test_%d", time.Now().UnixNano())
@ -172,7 +96,7 @@ func TestOlric_ConcurrentAccess(t *testing.T) {
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
go func(idx int) { go func(idx int) {
value := fmt.Sprintf("concurrent_value_%d", idx) value := fmt.Sprintf("concurrent_value_%d", idx)
err := putToOlric(env.GatewayURL, env.APIKey, dmap, key, value) err := e2e.PutToOlric(env.GatewayURL, env.APIKey, dmap, key, value)
done <- err done <- err
}(i) }(i)
} }
@ -188,7 +112,7 @@ func TestOlric_ConcurrentAccess(t *testing.T) {
require.Empty(t, errors, "FAIL: %d concurrent writes failed: %v", len(errors), errors) require.Empty(t, errors, "FAIL: %d concurrent writes failed: %v", len(errors), errors)
// The key should have ONE of the values (last write wins) // The key should have ONE of the values (last write wins)
retrieved, err := getFromOlric(env.GatewayURL, env.APIKey, dmap, key) retrieved, err := e2e.GetFromOlric(env.GatewayURL, env.APIKey, dmap, key)
require.NoError(t, err, "FAIL: Could not get key after concurrent writes") require.NoError(t, err, "FAIL: Could not get key after concurrent writes")
require.Contains(t, retrieved, "concurrent_value_", "FAIL: Value doesn't match expected pattern") require.Contains(t, retrieved, "concurrent_value_", "FAIL: Value doesn't match expected pattern")
@ -200,7 +124,7 @@ func TestOlric_ConcurrentAccess(t *testing.T) {
initialValue := "initial_value" initialValue := "initial_value"
// Set initial value // Set initial value
err := putToOlric(env.GatewayURL, env.APIKey, dmap, key, initialValue) err := e2e.PutToOlric(env.GatewayURL, env.APIKey, dmap, key, initialValue)
require.NoError(t, err, "FAIL: Could not set initial value") require.NoError(t, err, "FAIL: Could not set initial value")
// Launch concurrent readers and writers // Launch concurrent readers and writers
@ -209,7 +133,7 @@ func TestOlric_ConcurrentAccess(t *testing.T) {
// 10 readers // 10 readers
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
go func() { go func() {
_, err := getFromOlric(env.GatewayURL, env.APIKey, dmap, key) _, err := e2e.GetFromOlric(env.GatewayURL, env.APIKey, dmap, key)
done <- err done <- err
}() }()
} }
@ -218,7 +142,7 @@ func TestOlric_ConcurrentAccess(t *testing.T) {
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
go func(idx int) { go func(idx int) {
value := fmt.Sprintf("updated_value_%d", idx) value := fmt.Sprintf("updated_value_%d", idx)
err := putToOlric(env.GatewayURL, env.APIKey, dmap, key, value) err := e2e.PutToOlric(env.GatewayURL, env.APIKey, dmap, key, value)
done <- err done <- err
}(i) }(i)
} }
@ -247,7 +171,7 @@ func TestOlric_NamespaceClusterCache(t *testing.T) {
// Create a new namespace // Create a new namespace
namespace := fmt.Sprintf("cache-test-%d", time.Now().UnixNano()) namespace := fmt.Sprintf("cache-test-%d", time.Now().UnixNano())
env, err := LoadTestEnvWithNamespace(namespace) env, err := e2e.LoadTestEnvWithNamespace(namespace)
require.NoError(t, err, "FAIL: Could not create namespace for cache test") require.NoError(t, err, "FAIL: Could not create namespace for cache test")
require.NotEmpty(t, env.APIKey, "FAIL: No API key") require.NotEmpty(t, env.APIKey, "FAIL: No API key")
@ -260,11 +184,11 @@ func TestOlric_NamespaceClusterCache(t *testing.T) {
value := fmt.Sprintf("ns_value_%d", time.Now().UnixNano()) value := fmt.Sprintf("ns_value_%d", time.Now().UnixNano())
// Put using namespace API key // Put using namespace API key
err := putToOlric(env.GatewayURL, env.APIKey, dmap, key, value) err := e2e.PutToOlric(env.GatewayURL, env.APIKey, dmap, key, value)
require.NoError(t, err, "FAIL: Could not put value in namespace cache") require.NoError(t, err, "FAIL: Could not put value in namespace cache")
// Get // Get
retrieved, err := getFromOlric(env.GatewayURL, env.APIKey, dmap, key) retrieved, err := e2e.GetFromOlric(env.GatewayURL, env.APIKey, dmap, key)
require.NoError(t, err, "FAIL: Could not get value from namespace cache") require.NoError(t, err, "FAIL: Could not get value from namespace cache")
require.Equal(t, value, retrieved, "FAIL: Value mismatch in namespace cache") require.Equal(t, value, retrieved, "FAIL: Value mismatch in namespace cache")
@ -298,7 +222,7 @@ func TestOlric_NamespaceClusterCache(t *testing.T) {
// TestOlric_DataConsistency verifies data remains consistent across operations. // TestOlric_DataConsistency verifies data remains consistent across operations.
func TestOlric_DataConsistency(t *testing.T) { func TestOlric_DataConsistency(t *testing.T) {
env, err := LoadTestEnv() env, err := e2e.LoadTestEnv()
require.NoError(t, err, "FAIL: Could not load test environment") require.NoError(t, err, "FAIL: Could not load test environment")
dmap := fmt.Sprintf("consistency_test_%d", time.Now().UnixNano()) dmap := fmt.Sprintf("consistency_test_%d", time.Now().UnixNano())
@ -309,12 +233,12 @@ func TestOlric_DataConsistency(t *testing.T) {
// Write multiple times // Write multiple times
for i := 1; i <= 5; i++ { for i := 1; i <= 5; i++ {
value := fmt.Sprintf("version_%d", i) value := fmt.Sprintf("version_%d", i)
err := putToOlric(env.GatewayURL, env.APIKey, dmap, key, value) err := e2e.PutToOlric(env.GatewayURL, env.APIKey, dmap, key, value)
require.NoError(t, err, "FAIL: Could not update key to version %d", i) require.NoError(t, err, "FAIL: Could not update key to version %d", i)
} }
// Final read should return latest version // Final read should return latest version
retrieved, err := getFromOlric(env.GatewayURL, env.APIKey, dmap, key) retrieved, err := e2e.GetFromOlric(env.GatewayURL, env.APIKey, dmap, key)
require.NoError(t, err, "FAIL: Could not read final value") require.NoError(t, err, "FAIL: Could not read final value")
require.Equal(t, "version_5", retrieved, "FAIL: Latest version not preserved") require.Equal(t, "version_5", retrieved, "FAIL: Latest version not preserved")
@ -326,11 +250,11 @@ func TestOlric_DataConsistency(t *testing.T) {
value := "to_be_deleted" value := "to_be_deleted"
// Put // Put
err := putToOlric(env.GatewayURL, env.APIKey, dmap, key, value) err := e2e.PutToOlric(env.GatewayURL, env.APIKey, dmap, key, value)
require.NoError(t, err, "FAIL: Could not put value") require.NoError(t, err, "FAIL: Could not put value")
// Verify it exists // Verify it exists
retrieved, err := getFromOlric(env.GatewayURL, env.APIKey, dmap, key) retrieved, err := e2e.GetFromOlric(env.GatewayURL, env.APIKey, dmap, key)
require.NoError(t, err, "FAIL: Could not get value before delete") require.NoError(t, err, "FAIL: Could not get value before delete")
require.Equal(t, value, retrieved) require.Equal(t, value, retrieved)
@ -351,7 +275,7 @@ func TestOlric_DataConsistency(t *testing.T) {
"FAIL: Delete returned unexpected status %d", resp.StatusCode) "FAIL: Delete returned unexpected status %d", resp.StatusCode)
// Verify key is gone // Verify key is gone
_, err = getFromOlric(env.GatewayURL, env.APIKey, dmap, key) _, err = e2e.GetFromOlric(env.GatewayURL, env.APIKey, dmap, key)
require.Error(t, err, "FAIL: Key should not exist after delete") require.Error(t, err, "FAIL: Key should not exist after delete")
require.Contains(t, err.Error(), "not found", "FAIL: Expected 'not found' error") require.Contains(t, err.Error(), "not found", "FAIL: Expected 'not found' error")
@ -365,7 +289,7 @@ func TestOlric_DataConsistency(t *testing.T) {
func TestOlric_TTLExpiration(t *testing.T) { func TestOlric_TTLExpiration(t *testing.T) {
t.Skip("TTL support not yet implemented in cache handler - see set_handler.go lines 88-98") t.Skip("TTL support not yet implemented in cache handler - see set_handler.go lines 88-98")
env, err := LoadTestEnv() env, err := e2e.LoadTestEnv()
require.NoError(t, err, "FAIL: Could not load test environment") require.NoError(t, err, "FAIL: Could not load test environment")
dmap := fmt.Sprintf("ttl_test_%d", time.Now().UnixNano()) dmap := fmt.Sprintf("ttl_test_%d", time.Now().UnixNano())
@ -396,7 +320,7 @@ func TestOlric_TTLExpiration(t *testing.T) {
"FAIL: Put returned status %d", resp.StatusCode) "FAIL: Put returned status %d", resp.StatusCode)
// Verify key exists immediately // Verify key exists immediately
retrieved, err := getFromOlric(env.GatewayURL, env.APIKey, dmap, key) retrieved, err := e2e.GetFromOlric(env.GatewayURL, env.APIKey, dmap, key)
require.NoError(t, err, "FAIL: Could not get key immediately after put") require.NoError(t, err, "FAIL: Could not get key immediately after put")
require.Equal(t, value, retrieved) require.Equal(t, value, retrieved)
t.Logf(" Key exists immediately after put") t.Logf(" Key exists immediately after put")
@ -405,7 +329,7 @@ func TestOlric_TTLExpiration(t *testing.T) {
time.Sleep(time.Duration(ttlSeconds+2) * time.Second) time.Sleep(time.Duration(ttlSeconds+2) * time.Second)
// Key should be gone // Key should be gone
_, err = getFromOlric(env.GatewayURL, env.APIKey, dmap, key) _, err = e2e.GetFromOlric(env.GatewayURL, env.APIKey, dmap, key)
require.Error(t, err, "FAIL: Key should have expired after %d seconds", ttlSeconds) require.Error(t, err, "FAIL: Key should have expired after %d seconds", ttlSeconds)
require.Contains(t, err.Error(), "not found", "FAIL: Expected 'not found' error after TTL") require.Contains(t, err.Error(), "not found", "FAIL: Expected 'not found' error after TTL")

View File

@ -1,6 +1,6 @@
//go:build e2e //go:build e2e
package e2e package cluster_test
import ( import (
"context" "context"
@ -10,6 +10,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/DeBrosOfficial/network/e2e"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -21,15 +22,15 @@ import (
// TestRQLite_ClusterHealth verifies the RQLite cluster is healthy and operational. // TestRQLite_ClusterHealth verifies the RQLite cluster is healthy and operational.
func TestRQLite_ClusterHealth(t *testing.T) { func TestRQLite_ClusterHealth(t *testing.T) {
SkipIfMissingGateway(t) e2e.SkipIfMissingGateway(t)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel() defer cancel()
// Check RQLite schema endpoint (proves cluster is reachable) // Check RQLite schema endpoint (proves cluster is reachable)
req := &HTTPRequest{ req := &e2e.HTTPRequest{
Method: http.MethodGet, Method: http.MethodGet,
URL: GetGatewayURL() + "/v1/rqlite/schema", URL: e2e.GetGatewayURL() + "/v1/rqlite/schema",
} }
body, status, err := req.Do(ctx) body, status, err := req.Do(ctx)
@ -37,7 +38,7 @@ func TestRQLite_ClusterHealth(t *testing.T) {
require.Equal(t, http.StatusOK, status, "FAIL: RQLite schema endpoint returned %d: %s", status, string(body)) require.Equal(t, http.StatusOK, status, "FAIL: RQLite schema endpoint returned %d: %s", status, string(body))
var schemaResp map[string]interface{} var schemaResp map[string]interface{}
err = DecodeJSON(body, &schemaResp) err = e2e.DecodeJSON(body, &schemaResp)
require.NoError(t, err, "FAIL: Could not decode RQLite schema response") require.NoError(t, err, "FAIL: Could not decode RQLite schema response")
// Schema endpoint should return tables array // Schema endpoint should return tables array
@ -49,27 +50,27 @@ func TestRQLite_ClusterHealth(t *testing.T) {
// TestRQLite_WriteReadConsistency verifies data written can be read back consistently. // TestRQLite_WriteReadConsistency verifies data written can be read back consistently.
func TestRQLite_WriteReadConsistency(t *testing.T) { func TestRQLite_WriteReadConsistency(t *testing.T) {
SkipIfMissingGateway(t) e2e.SkipIfMissingGateway(t)
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel() defer cancel()
table := GenerateTableName() table := e2e.GenerateTableName()
// Cleanup // Cleanup
defer func() { defer func() {
dropReq := &HTTPRequest{ dropReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/rqlite/drop-table", URL: e2e.GetGatewayURL() + "/v1/rqlite/drop-table",
Body: map[string]interface{}{"table": table}, Body: map[string]interface{}{"table": table},
} }
dropReq.Do(context.Background()) dropReq.Do(context.Background())
}() }()
// Create table // Create table
createReq := &HTTPRequest{ createReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/rqlite/create-table", URL: e2e.GetGatewayURL() + "/v1/rqlite/create-table",
Body: map[string]interface{}{ Body: map[string]interface{}{
"schema": fmt.Sprintf( "schema": fmt.Sprintf(
"CREATE TABLE IF NOT EXISTS %s (id INTEGER PRIMARY KEY, value TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP)", "CREATE TABLE IF NOT EXISTS %s (id INTEGER PRIMARY KEY, value TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP)",
@ -88,9 +89,9 @@ func TestRQLite_WriteReadConsistency(t *testing.T) {
uniqueValue := fmt.Sprintf("test_value_%d", time.Now().UnixNano()) uniqueValue := fmt.Sprintf("test_value_%d", time.Now().UnixNano())
// Insert // Insert
insertReq := &HTTPRequest{ insertReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/rqlite/transaction", URL: e2e.GetGatewayURL() + "/v1/rqlite/transaction",
Body: map[string]interface{}{ Body: map[string]interface{}{
"statements": []string{ "statements": []string{
fmt.Sprintf("INSERT INTO %s (value) VALUES ('%s')", table, uniqueValue), fmt.Sprintf("INSERT INTO %s (value) VALUES ('%s')", table, uniqueValue),
@ -103,9 +104,9 @@ func TestRQLite_WriteReadConsistency(t *testing.T) {
require.Equal(t, http.StatusOK, status, "FAIL: Insert returned status %d", status) require.Equal(t, http.StatusOK, status, "FAIL: Insert returned status %d", status)
// Read back // Read back
queryReq := &HTTPRequest{ queryReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/rqlite/query", URL: e2e.GetGatewayURL() + "/v1/rqlite/query",
Body: map[string]interface{}{ Body: map[string]interface{}{
"sql": fmt.Sprintf("SELECT value FROM %s WHERE value = '%s'", table, uniqueValue), "sql": fmt.Sprintf("SELECT value FROM %s WHERE value = '%s'", table, uniqueValue),
}, },
@ -116,7 +117,7 @@ func TestRQLite_WriteReadConsistency(t *testing.T) {
require.Equal(t, http.StatusOK, status, "FAIL: Query returned status %d", status) require.Equal(t, http.StatusOK, status, "FAIL: Query returned status %d", status)
var queryResp map[string]interface{} var queryResp map[string]interface{}
err = DecodeJSON(body, &queryResp) err = e2e.DecodeJSON(body, &queryResp)
require.NoError(t, err, "FAIL: Could not decode query response") require.NoError(t, err, "FAIL: Could not decode query response")
// Verify we got our value back // Verify we got our value back
@ -135,9 +136,9 @@ func TestRQLite_WriteReadConsistency(t *testing.T) {
fmt.Sprintf("INSERT INTO %s (value) VALUES ('batch_%d')", table, i)) fmt.Sprintf("INSERT INTO %s (value) VALUES ('batch_%d')", table, i))
} }
insertReq := &HTTPRequest{ insertReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/rqlite/transaction", URL: e2e.GetGatewayURL() + "/v1/rqlite/transaction",
Body: map[string]interface{}{ Body: map[string]interface{}{
"statements": statements, "statements": statements,
}, },
@ -148,9 +149,9 @@ func TestRQLite_WriteReadConsistency(t *testing.T) {
require.Equal(t, http.StatusOK, status, "FAIL: Batch insert returned status %d", status) require.Equal(t, http.StatusOK, status, "FAIL: Batch insert returned status %d", status)
// Count all batch rows // Count all batch rows
queryReq := &HTTPRequest{ queryReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/rqlite/query", URL: e2e.GetGatewayURL() + "/v1/rqlite/query",
Body: map[string]interface{}{ Body: map[string]interface{}{
"sql": fmt.Sprintf("SELECT COUNT(*) as cnt FROM %s WHERE value LIKE 'batch_%%'", table), "sql": fmt.Sprintf("SELECT COUNT(*) as cnt FROM %s WHERE value LIKE 'batch_%%'", table),
}, },
@ -161,7 +162,7 @@ func TestRQLite_WriteReadConsistency(t *testing.T) {
require.Equal(t, http.StatusOK, status, "FAIL: Count query returned status %d", status) require.Equal(t, http.StatusOK, status, "FAIL: Count query returned status %d", status)
var queryResp map[string]interface{} var queryResp map[string]interface{}
DecodeJSON(body, &queryResp) e2e.DecodeJSON(body, &queryResp)
if rows, ok := queryResp["rows"].([]interface{}); ok && len(rows) > 0 { if rows, ok := queryResp["rows"].([]interface{}); ok && len(rows) > 0 {
row := rows[0].([]interface{}) row := rows[0].([]interface{})
@ -175,27 +176,27 @@ func TestRQLite_WriteReadConsistency(t *testing.T) {
// TestRQLite_TransactionAtomicity verifies transactions are atomic. // TestRQLite_TransactionAtomicity verifies transactions are atomic.
func TestRQLite_TransactionAtomicity(t *testing.T) { func TestRQLite_TransactionAtomicity(t *testing.T) {
SkipIfMissingGateway(t) e2e.SkipIfMissingGateway(t)
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel() defer cancel()
table := GenerateTableName() table := e2e.GenerateTableName()
// Cleanup // Cleanup
defer func() { defer func() {
dropReq := &HTTPRequest{ dropReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/rqlite/drop-table", URL: e2e.GetGatewayURL() + "/v1/rqlite/drop-table",
Body: map[string]interface{}{"table": table}, Body: map[string]interface{}{"table": table},
} }
dropReq.Do(context.Background()) dropReq.Do(context.Background())
}() }()
// Create table // Create table
createReq := &HTTPRequest{ createReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/rqlite/create-table", URL: e2e.GetGatewayURL() + "/v1/rqlite/create-table",
Body: map[string]interface{}{ Body: map[string]interface{}{
"schema": fmt.Sprintf( "schema": fmt.Sprintf(
"CREATE TABLE IF NOT EXISTS %s (id INTEGER PRIMARY KEY, value TEXT UNIQUE)", "CREATE TABLE IF NOT EXISTS %s (id INTEGER PRIMARY KEY, value TEXT UNIQUE)",
@ -210,9 +211,9 @@ func TestRQLite_TransactionAtomicity(t *testing.T) {
"FAIL: Create table returned status %d", status) "FAIL: Create table returned status %d", status)
t.Run("Successful_transaction_commits_all", func(t *testing.T) { t.Run("Successful_transaction_commits_all", func(t *testing.T) {
txReq := &HTTPRequest{ txReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/rqlite/transaction", URL: e2e.GetGatewayURL() + "/v1/rqlite/transaction",
Body: map[string]interface{}{ Body: map[string]interface{}{
"statements": []string{ "statements": []string{
fmt.Sprintf("INSERT INTO %s (value) VALUES ('tx_val_1')", table), fmt.Sprintf("INSERT INTO %s (value) VALUES ('tx_val_1')", table),
@ -227,9 +228,9 @@ func TestRQLite_TransactionAtomicity(t *testing.T) {
require.Equal(t, http.StatusOK, status, "FAIL: Transaction returned status %d", status) require.Equal(t, http.StatusOK, status, "FAIL: Transaction returned status %d", status)
// Verify all 3 rows exist // Verify all 3 rows exist
queryReq := &HTTPRequest{ queryReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/rqlite/query", URL: e2e.GetGatewayURL() + "/v1/rqlite/query",
Body: map[string]interface{}{ Body: map[string]interface{}{
"sql": fmt.Sprintf("SELECT COUNT(*) FROM %s WHERE value LIKE 'tx_val_%%'", table), "sql": fmt.Sprintf("SELECT COUNT(*) FROM %s WHERE value LIKE 'tx_val_%%'", table),
}, },
@ -237,7 +238,7 @@ func TestRQLite_TransactionAtomicity(t *testing.T) {
body, _, _ := queryReq.Do(ctx) body, _, _ := queryReq.Do(ctx)
var queryResp map[string]interface{} var queryResp map[string]interface{}
DecodeJSON(body, &queryResp) e2e.DecodeJSON(body, &queryResp)
if rows, ok := queryResp["rows"].([]interface{}); ok && len(rows) > 0 { if rows, ok := queryResp["rows"].([]interface{}); ok && len(rows) > 0 {
row := rows[0].([]interface{}) row := rows[0].([]interface{})
@ -250,9 +251,9 @@ func TestRQLite_TransactionAtomicity(t *testing.T) {
t.Run("Updates_preserve_consistency", func(t *testing.T) { t.Run("Updates_preserve_consistency", func(t *testing.T) {
// Update a value // Update a value
updateReq := &HTTPRequest{ updateReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/rqlite/transaction", URL: e2e.GetGatewayURL() + "/v1/rqlite/transaction",
Body: map[string]interface{}{ Body: map[string]interface{}{
"statements": []string{ "statements": []string{
fmt.Sprintf("UPDATE %s SET value = 'tx_val_1_updated' WHERE value = 'tx_val_1'", table), fmt.Sprintf("UPDATE %s SET value = 'tx_val_1_updated' WHERE value = 'tx_val_1'", table),
@ -265,9 +266,9 @@ func TestRQLite_TransactionAtomicity(t *testing.T) {
require.Equal(t, http.StatusOK, status, "FAIL: Update returned status %d", status) require.Equal(t, http.StatusOK, status, "FAIL: Update returned status %d", status)
// Verify update took effect // Verify update took effect
queryReq := &HTTPRequest{ queryReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/rqlite/query", URL: e2e.GetGatewayURL() + "/v1/rqlite/query",
Body: map[string]interface{}{ Body: map[string]interface{}{
"sql": fmt.Sprintf("SELECT value FROM %s WHERE value = 'tx_val_1_updated'", table), "sql": fmt.Sprintf("SELECT value FROM %s WHERE value = 'tx_val_1_updated'", table),
}, },
@ -275,7 +276,7 @@ func TestRQLite_TransactionAtomicity(t *testing.T) {
body, _, _ := queryReq.Do(ctx) body, _, _ := queryReq.Do(ctx)
var queryResp map[string]interface{} var queryResp map[string]interface{}
DecodeJSON(body, &queryResp) e2e.DecodeJSON(body, &queryResp)
count, _ := queryResp["count"].(float64) count, _ := queryResp["count"].(float64)
require.Equal(t, float64(1), count, "FAIL: Update didn't take effect") require.Equal(t, float64(1), count, "FAIL: Update didn't take effect")
@ -286,27 +287,27 @@ func TestRQLite_TransactionAtomicity(t *testing.T) {
// TestRQLite_ConcurrentWrites verifies the cluster handles concurrent writes correctly. // TestRQLite_ConcurrentWrites verifies the cluster handles concurrent writes correctly.
func TestRQLite_ConcurrentWrites(t *testing.T) { func TestRQLite_ConcurrentWrites(t *testing.T) {
SkipIfMissingGateway(t) e2e.SkipIfMissingGateway(t)
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel() defer cancel()
table := GenerateTableName() table := e2e.GenerateTableName()
// Cleanup // Cleanup
defer func() { defer func() {
dropReq := &HTTPRequest{ dropReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/rqlite/drop-table", URL: e2e.GetGatewayURL() + "/v1/rqlite/drop-table",
Body: map[string]interface{}{"table": table}, Body: map[string]interface{}{"table": table},
} }
dropReq.Do(context.Background()) dropReq.Do(context.Background())
}() }()
// Create table // Create table
createReq := &HTTPRequest{ createReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/rqlite/create-table", URL: e2e.GetGatewayURL() + "/v1/rqlite/create-table",
Body: map[string]interface{}{ Body: map[string]interface{}{
"schema": fmt.Sprintf( "schema": fmt.Sprintf(
"CREATE TABLE IF NOT EXISTS %s (id INTEGER PRIMARY KEY, worker INTEGER, seq INTEGER)", "CREATE TABLE IF NOT EXISTS %s (id INTEGER PRIMARY KEY, worker INTEGER, seq INTEGER)",
@ -333,9 +334,9 @@ func TestRQLite_ConcurrentWrites(t *testing.T) {
go func(workerID int) { go func(workerID int) {
defer wg.Done() defer wg.Done()
for i := 0; i < insertsPerWorker; i++ { for i := 0; i < insertsPerWorker; i++ {
insertReq := &HTTPRequest{ insertReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/rqlite/transaction", URL: e2e.GetGatewayURL() + "/v1/rqlite/transaction",
Body: map[string]interface{}{ Body: map[string]interface{}{
"statements": []string{ "statements": []string{
fmt.Sprintf("INSERT INTO %s (worker, seq) VALUES (%d, %d)", table, workerID, i), fmt.Sprintf("INSERT INTO %s (worker, seq) VALUES (%d, %d)", table, workerID, i),
@ -367,9 +368,9 @@ func TestRQLite_ConcurrentWrites(t *testing.T) {
require.Empty(t, errors, "FAIL: %d concurrent inserts failed: %v", len(errors), errors) require.Empty(t, errors, "FAIL: %d concurrent inserts failed: %v", len(errors), errors)
// Verify total count // Verify total count
queryReq := &HTTPRequest{ queryReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/rqlite/query", URL: e2e.GetGatewayURL() + "/v1/rqlite/query",
Body: map[string]interface{}{ Body: map[string]interface{}{
"sql": fmt.Sprintf("SELECT COUNT(*) FROM %s", table), "sql": fmt.Sprintf("SELECT COUNT(*) FROM %s", table),
}, },
@ -377,7 +378,7 @@ func TestRQLite_ConcurrentWrites(t *testing.T) {
body, _, _ := queryReq.Do(ctx) body, _, _ := queryReq.Do(ctx)
var queryResp map[string]interface{} var queryResp map[string]interface{}
DecodeJSON(body, &queryResp) e2e.DecodeJSON(body, &queryResp)
if rows, ok := queryResp["rows"].([]interface{}); ok && len(rows) > 0 { if rows, ok := queryResp["rows"].([]interface{}); ok && len(rows) > 0 {
row := rows[0].([]interface{}) row := rows[0].([]interface{})
@ -395,7 +396,7 @@ func TestRQLite_NamespaceClusterOperations(t *testing.T) {
// Create a new namespace // Create a new namespace
namespace := fmt.Sprintf("rqlite-test-%d", time.Now().UnixNano()) namespace := fmt.Sprintf("rqlite-test-%d", time.Now().UnixNano())
env, err := LoadTestEnvWithNamespace(namespace) env, err := e2e.LoadTestEnvWithNamespace(namespace)
require.NoError(t, err, "FAIL: Could not create namespace for RQLite test") require.NoError(t, err, "FAIL: Could not create namespace for RQLite test")
require.NotEmpty(t, env.APIKey, "FAIL: No API key - namespace provisioning failed") require.NotEmpty(t, env.APIKey, "FAIL: No API key - namespace provisioning failed")
@ -404,11 +405,11 @@ func TestRQLite_NamespaceClusterOperations(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel() defer cancel()
table := GenerateTableName() table := e2e.GenerateTableName()
// Cleanup // Cleanup
defer func() { defer func() {
dropReq := &HTTPRequest{ dropReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: env.GatewayURL + "/v1/rqlite/drop-table", URL: env.GatewayURL + "/v1/rqlite/drop-table",
Body: map[string]interface{}{"table": table}, Body: map[string]interface{}{"table": table},
@ -419,7 +420,7 @@ func TestRQLite_NamespaceClusterOperations(t *testing.T) {
t.Run("Namespace_RQLite_create_insert_query", func(t *testing.T) { t.Run("Namespace_RQLite_create_insert_query", func(t *testing.T) {
// Create table in namespace cluster // Create table in namespace cluster
createReq := &HTTPRequest{ createReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: env.GatewayURL + "/v1/rqlite/create-table", URL: env.GatewayURL + "/v1/rqlite/create-table",
Headers: map[string]string{"Authorization": "Bearer " + env.APIKey}, Headers: map[string]string{"Authorization": "Bearer " + env.APIKey},
@ -438,7 +439,7 @@ func TestRQLite_NamespaceClusterOperations(t *testing.T) {
// Insert data // Insert data
uniqueValue := fmt.Sprintf("ns_value_%d", time.Now().UnixNano()) uniqueValue := fmt.Sprintf("ns_value_%d", time.Now().UnixNano())
insertReq := &HTTPRequest{ insertReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: env.GatewayURL + "/v1/rqlite/transaction", URL: env.GatewayURL + "/v1/rqlite/transaction",
Headers: map[string]string{"Authorization": "Bearer " + env.APIKey}, Headers: map[string]string{"Authorization": "Bearer " + env.APIKey},
@ -454,7 +455,7 @@ func TestRQLite_NamespaceClusterOperations(t *testing.T) {
require.Equal(t, http.StatusOK, status, "FAIL: Insert returned status %d", status) require.Equal(t, http.StatusOK, status, "FAIL: Insert returned status %d", status)
// Query data // Query data
queryReq := &HTTPRequest{ queryReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: env.GatewayURL + "/v1/rqlite/query", URL: env.GatewayURL + "/v1/rqlite/query",
Headers: map[string]string{"Authorization": "Bearer " + env.APIKey}, Headers: map[string]string{"Authorization": "Bearer " + env.APIKey},
@ -468,7 +469,7 @@ func TestRQLite_NamespaceClusterOperations(t *testing.T) {
require.Equal(t, http.StatusOK, status, "FAIL: Query returned status %d", status) require.Equal(t, http.StatusOK, status, "FAIL: Query returned status %d", status)
var queryResp map[string]interface{} var queryResp map[string]interface{}
DecodeJSON(body, &queryResp) e2e.DecodeJSON(body, &queryResp)
count, _ := queryResp["count"].(float64) count, _ := queryResp["count"].(float64)
require.Equal(t, float64(1), count, "FAIL: Data not found in namespace cluster") require.Equal(t, float64(1), count, "FAIL: Data not found in namespace cluster")

View File

@ -1536,6 +1536,81 @@ func TestDeploymentWithHostHeader(t *testing.T, env *E2ETestEnv, host, path stri
return resp return resp
} }
// PutToOlric stores a key-value pair in Olric via the gateway HTTP API
func PutToOlric(gatewayURL, apiKey, dmap, key, value string) error {
reqBody := map[string]interface{}{
"dmap": dmap,
"key": key,
"value": value,
}
bodyBytes, _ := json.Marshal(reqBody)
req, err := http.NewRequest("POST", gatewayURL+"/v1/cache/put", strings.NewReader(string(bodyBytes)))
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+apiKey)
client := &http.Client{Timeout: 10 * time.Second}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated {
body, _ := io.ReadAll(resp.Body)
return fmt.Errorf("put failed with status %d: %s", resp.StatusCode, string(body))
}
return nil
}
// GetFromOlric retrieves a value from Olric via the gateway HTTP API
func GetFromOlric(gatewayURL, apiKey, dmap, key string) (string, error) {
reqBody := map[string]interface{}{
"dmap": dmap,
"key": key,
}
bodyBytes, _ := json.Marshal(reqBody)
req, err := http.NewRequest("POST", gatewayURL+"/v1/cache/get", strings.NewReader(string(bodyBytes)))
if err != nil {
return "", err
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+apiKey)
client := &http.Client{Timeout: 10 * time.Second}
resp, err := client.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
if resp.StatusCode == http.StatusNotFound {
return "", fmt.Errorf("key not found")
}
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return "", fmt.Errorf("get failed with status %d: %s", resp.StatusCode, string(body))
}
body, _ := io.ReadAll(resp.Body)
var result map[string]interface{}
if err := json.Unmarshal(body, &result); err != nil {
return "", err
}
if value, ok := result["value"].(string); ok {
return value, nil
}
if value, ok := result["value"]; ok {
return fmt.Sprintf("%v", value), nil
}
return "", fmt.Errorf("value not found in response")
}
// WaitForHealthy waits for a deployment to become healthy // WaitForHealthy waits for a deployment to become healthy
func WaitForHealthy(t *testing.T, env *E2ETestEnv, deploymentID string, timeout time.Duration) bool { func WaitForHealthy(t *testing.T, env *E2ETestEnv, deploymentID string, timeout time.Duration) bool {
t.Helper() t.Helper()

View File

@ -1,6 +1,6 @@
//go:build e2e //go:build e2e
package e2e package integration_test
import ( import (
"context" "context"
@ -10,16 +10,18 @@ import (
"sync/atomic" "sync/atomic"
"testing" "testing"
"time" "time"
"github.com/DeBrosOfficial/network/e2e"
) )
// TestCache_ConcurrentWrites tests concurrent cache writes // TestCache_ConcurrentWrites tests concurrent cache writes
func TestCache_ConcurrentWrites(t *testing.T) { func TestCache_ConcurrentWrites(t *testing.T) {
SkipIfMissingGateway(t) e2e.SkipIfMissingGateway(t)
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel() defer cancel()
dmap := GenerateDMapName() dmap := e2e.GenerateDMapName()
numGoroutines := 10 numGoroutines := 10
var wg sync.WaitGroup var wg sync.WaitGroup
var errorCount int32 var errorCount int32
@ -32,9 +34,9 @@ func TestCache_ConcurrentWrites(t *testing.T) {
key := fmt.Sprintf("key-%d", idx) key := fmt.Sprintf("key-%d", idx)
value := fmt.Sprintf("value-%d", idx) value := fmt.Sprintf("value-%d", idx)
putReq := &HTTPRequest{ putReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/cache/put", URL: e2e.GetGatewayURL() + "/v1/cache/put",
Body: map[string]interface{}{ Body: map[string]interface{}{
"dmap": dmap, "dmap": dmap,
"key": key, "key": key,
@ -56,9 +58,9 @@ func TestCache_ConcurrentWrites(t *testing.T) {
} }
// Verify all values exist // Verify all values exist
scanReq := &HTTPRequest{ scanReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/cache/scan", URL: e2e.GetGatewayURL() + "/v1/cache/scan",
Body: map[string]interface{}{ Body: map[string]interface{}{
"dmap": dmap, "dmap": dmap,
}, },
@ -70,7 +72,7 @@ func TestCache_ConcurrentWrites(t *testing.T) {
} }
var scanResp map[string]interface{} var scanResp map[string]interface{}
if err := DecodeJSON(body, &scanResp); err != nil { if err := e2e.DecodeJSON(body, &scanResp); err != nil {
t.Fatalf("failed to decode response: %v", err) t.Fatalf("failed to decode response: %v", err)
} }
@ -82,19 +84,19 @@ func TestCache_ConcurrentWrites(t *testing.T) {
// TestCache_ConcurrentReads tests concurrent cache reads // TestCache_ConcurrentReads tests concurrent cache reads
func TestCache_ConcurrentReads(t *testing.T) { func TestCache_ConcurrentReads(t *testing.T) {
SkipIfMissingGateway(t) e2e.SkipIfMissingGateway(t)
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel() defer cancel()
dmap := GenerateDMapName() dmap := e2e.GenerateDMapName()
key := "shared-key" key := "shared-key"
value := "shared-value" value := "shared-value"
// Put value first // Put value first
putReq := &HTTPRequest{ putReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/cache/put", URL: e2e.GetGatewayURL() + "/v1/cache/put",
Body: map[string]interface{}{ Body: map[string]interface{}{
"dmap": dmap, "dmap": dmap,
"key": key, "key": key,
@ -117,9 +119,9 @@ func TestCache_ConcurrentReads(t *testing.T) {
go func() { go func() {
defer wg.Done() defer wg.Done()
getReq := &HTTPRequest{ getReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/cache/get", URL: e2e.GetGatewayURL() + "/v1/cache/get",
Body: map[string]interface{}{ Body: map[string]interface{}{
"dmap": dmap, "dmap": dmap,
"key": key, "key": key,
@ -133,7 +135,7 @@ func TestCache_ConcurrentReads(t *testing.T) {
} }
var getResp map[string]interface{} var getResp map[string]interface{}
if err := DecodeJSON(body, &getResp); err != nil { if err := e2e.DecodeJSON(body, &getResp); err != nil {
atomic.AddInt32(&errorCount, 1) atomic.AddInt32(&errorCount, 1)
return return
} }
@ -153,12 +155,12 @@ func TestCache_ConcurrentReads(t *testing.T) {
// TestCache_ConcurrentDeleteAndWrite tests concurrent delete and write // TestCache_ConcurrentDeleteAndWrite tests concurrent delete and write
func TestCache_ConcurrentDeleteAndWrite(t *testing.T) { func TestCache_ConcurrentDeleteAndWrite(t *testing.T) {
SkipIfMissingGateway(t) e2e.SkipIfMissingGateway(t)
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel() defer cancel()
dmap := GenerateDMapName() dmap := e2e.GenerateDMapName()
var wg sync.WaitGroup var wg sync.WaitGroup
var errorCount int32 var errorCount int32
@ -174,9 +176,9 @@ func TestCache_ConcurrentDeleteAndWrite(t *testing.T) {
key := fmt.Sprintf("key-%d", idx) key := fmt.Sprintf("key-%d", idx)
value := fmt.Sprintf("value-%d", idx) value := fmt.Sprintf("value-%d", idx)
putReq := &HTTPRequest{ putReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/cache/put", URL: e2e.GetGatewayURL() + "/v1/cache/put",
Body: map[string]interface{}{ Body: map[string]interface{}{
"dmap": dmap, "dmap": dmap,
"key": key, "key": key,
@ -201,9 +203,9 @@ func TestCache_ConcurrentDeleteAndWrite(t *testing.T) {
key := fmt.Sprintf("key-%d", idx) key := fmt.Sprintf("key-%d", idx)
deleteReq := &HTTPRequest{ deleteReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/cache/delete", URL: e2e.GetGatewayURL() + "/v1/cache/delete",
Body: map[string]interface{}{ Body: map[string]interface{}{
"dmap": dmap, "dmap": dmap,
"key": key, "key": key,
@ -226,18 +228,18 @@ func TestCache_ConcurrentDeleteAndWrite(t *testing.T) {
// TestRQLite_ConcurrentInserts tests concurrent database inserts // TestRQLite_ConcurrentInserts tests concurrent database inserts
func TestRQLite_ConcurrentInserts(t *testing.T) { func TestRQLite_ConcurrentInserts(t *testing.T) {
SkipIfMissingGateway(t) e2e.SkipIfMissingGateway(t)
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel() defer cancel()
table := GenerateTableName() table := e2e.GenerateTableName()
// Cleanup table after test // Cleanup table after test
defer func() { defer func() {
dropReq := &HTTPRequest{ dropReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/rqlite/drop-table", URL: e2e.GetGatewayURL() + "/v1/rqlite/drop-table",
Body: map[string]interface{}{"table": table}, Body: map[string]interface{}{"table": table},
} }
dropReq.Do(context.Background()) dropReq.Do(context.Background())
@ -249,9 +251,9 @@ func TestRQLite_ConcurrentInserts(t *testing.T) {
) )
// Create table // Create table
createReq := &HTTPRequest{ createReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/rqlite/create-table", URL: e2e.GetGatewayURL() + "/v1/rqlite/create-table",
Body: map[string]interface{}{ Body: map[string]interface{}{
"schema": schema, "schema": schema,
}, },
@ -272,9 +274,9 @@ func TestRQLite_ConcurrentInserts(t *testing.T) {
go func(idx int) { go func(idx int) {
defer wg.Done() defer wg.Done()
txReq := &HTTPRequest{ txReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/rqlite/transaction", URL: e2e.GetGatewayURL() + "/v1/rqlite/transaction",
Body: map[string]interface{}{ Body: map[string]interface{}{
"statements": []string{ "statements": []string{
fmt.Sprintf("INSERT INTO %s(value) VALUES (%d)", table, idx), fmt.Sprintf("INSERT INTO %s(value) VALUES (%d)", table, idx),
@ -296,9 +298,9 @@ func TestRQLite_ConcurrentInserts(t *testing.T) {
} }
// Verify count // Verify count
queryReq := &HTTPRequest{ queryReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/rqlite/query", URL: e2e.GetGatewayURL() + "/v1/rqlite/query",
Body: map[string]interface{}{ Body: map[string]interface{}{
"sql": fmt.Sprintf("SELECT COUNT(*) as count FROM %s", table), "sql": fmt.Sprintf("SELECT COUNT(*) as count FROM %s", table),
}, },
@ -310,7 +312,7 @@ func TestRQLite_ConcurrentInserts(t *testing.T) {
} }
var countResp map[string]interface{} var countResp map[string]interface{}
if err := DecodeJSON(body, &countResp); err != nil { if err := e2e.DecodeJSON(body, &countResp); err != nil {
t.Fatalf("failed to decode response: %v", err) t.Fatalf("failed to decode response: %v", err)
} }
@ -325,18 +327,18 @@ func TestRQLite_ConcurrentInserts(t *testing.T) {
// TestRQLite_LargeBatchTransaction tests a large transaction with many statements // TestRQLite_LargeBatchTransaction tests a large transaction with many statements
func TestRQLite_LargeBatchTransaction(t *testing.T) { func TestRQLite_LargeBatchTransaction(t *testing.T) {
SkipIfMissingGateway(t) e2e.SkipIfMissingGateway(t)
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel() defer cancel()
table := GenerateTableName() table := e2e.GenerateTableName()
// Cleanup table after test // Cleanup table after test
defer func() { defer func() {
dropReq := &HTTPRequest{ dropReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/rqlite/drop-table", URL: e2e.GetGatewayURL() + "/v1/rqlite/drop-table",
Body: map[string]interface{}{"table": table}, Body: map[string]interface{}{"table": table},
} }
dropReq.Do(context.Background()) dropReq.Do(context.Background())
@ -348,9 +350,9 @@ func TestRQLite_LargeBatchTransaction(t *testing.T) {
) )
// Create table // Create table
createReq := &HTTPRequest{ createReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/rqlite/create-table", URL: e2e.GetGatewayURL() + "/v1/rqlite/create-table",
Body: map[string]interface{}{ Body: map[string]interface{}{
"schema": schema, "schema": schema,
}, },
@ -370,9 +372,9 @@ func TestRQLite_LargeBatchTransaction(t *testing.T) {
}) })
} }
txReq := &HTTPRequest{ txReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/rqlite/transaction", URL: e2e.GetGatewayURL() + "/v1/rqlite/transaction",
Body: map[string]interface{}{ Body: map[string]interface{}{
"ops": ops, "ops": ops,
}, },
@ -384,9 +386,9 @@ func TestRQLite_LargeBatchTransaction(t *testing.T) {
} }
// Verify count // Verify count
queryReq := &HTTPRequest{ queryReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/rqlite/query", URL: e2e.GetGatewayURL() + "/v1/rqlite/query",
Body: map[string]interface{}{ Body: map[string]interface{}{
"sql": fmt.Sprintf("SELECT COUNT(*) as count FROM %s", table), "sql": fmt.Sprintf("SELECT COUNT(*) as count FROM %s", table),
}, },
@ -398,7 +400,7 @@ func TestRQLite_LargeBatchTransaction(t *testing.T) {
} }
var countResp map[string]interface{} var countResp map[string]interface{}
if err := DecodeJSON(body, &countResp); err != nil { if err := e2e.DecodeJSON(body, &countResp); err != nil {
t.Fatalf("failed to decode response: %v", err) t.Fatalf("failed to decode response: %v", err)
} }
@ -412,19 +414,19 @@ func TestRQLite_LargeBatchTransaction(t *testing.T) {
// TestCache_TTLExpiryWithSleep tests TTL expiry with a controlled sleep // TestCache_TTLExpiryWithSleep tests TTL expiry with a controlled sleep
func TestCache_TTLExpiryWithSleep(t *testing.T) { func TestCache_TTLExpiryWithSleep(t *testing.T) {
SkipIfMissingGateway(t) e2e.SkipIfMissingGateway(t)
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel() defer cancel()
dmap := GenerateDMapName() dmap := e2e.GenerateDMapName()
key := "ttl-expiry-key" key := "ttl-expiry-key"
value := "ttl-expiry-value" value := "ttl-expiry-value"
// Put value with 2 second TTL // Put value with 2 second TTL
putReq := &HTTPRequest{ putReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/cache/put", URL: e2e.GetGatewayURL() + "/v1/cache/put",
Body: map[string]interface{}{ Body: map[string]interface{}{
"dmap": dmap, "dmap": dmap,
"key": key, "key": key,
@ -439,9 +441,9 @@ func TestCache_TTLExpiryWithSleep(t *testing.T) {
} }
// Verify exists immediately // Verify exists immediately
getReq := &HTTPRequest{ getReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/cache/get", URL: e2e.GetGatewayURL() + "/v1/cache/get",
Body: map[string]interface{}{ Body: map[string]interface{}{
"dmap": dmap, "dmap": dmap,
"key": key, "key": key,
@ -454,7 +456,7 @@ func TestCache_TTLExpiryWithSleep(t *testing.T) {
} }
// Sleep for TTL duration + buffer // Sleep for TTL duration + buffer
Delay(2500) e2e.Delay(2500)
// Try to get after TTL expires // Try to get after TTL expires
_, status, err = getReq.Do(ctx) _, status, err = getReq.Do(ctx)
@ -465,21 +467,21 @@ func TestCache_TTLExpiryWithSleep(t *testing.T) {
// TestCache_ConcurrentWriteAndDelete tests concurrent writes and deletes on same key // TestCache_ConcurrentWriteAndDelete tests concurrent writes and deletes on same key
func TestCache_ConcurrentWriteAndDelete(t *testing.T) { func TestCache_ConcurrentWriteAndDelete(t *testing.T) {
SkipIfMissingGateway(t) e2e.SkipIfMissingGateway(t)
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel() defer cancel()
dmap := GenerateDMapName() dmap := e2e.GenerateDMapName()
key := "contested-key" key := "contested-key"
// Alternate between writes and deletes // Alternate between writes and deletes
numIterations := 5 numIterations := 5
for i := 0; i < numIterations; i++ { for i := 0; i < numIterations; i++ {
// Write // Write
putReq := &HTTPRequest{ putReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/cache/put", URL: e2e.GetGatewayURL() + "/v1/cache/put",
Body: map[string]interface{}{ Body: map[string]interface{}{
"dmap": dmap, "dmap": dmap,
"key": key, "key": key,
@ -493,9 +495,9 @@ func TestCache_ConcurrentWriteAndDelete(t *testing.T) {
} }
// Read // Read
getReq := &HTTPRequest{ getReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/cache/get", URL: e2e.GetGatewayURL() + "/v1/cache/get",
Body: map[string]interface{}{ Body: map[string]interface{}{
"dmap": dmap, "dmap": dmap,
"key": key, "key": key,
@ -508,9 +510,9 @@ func TestCache_ConcurrentWriteAndDelete(t *testing.T) {
} }
// Delete // Delete
deleteReq := &HTTPRequest{ deleteReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/cache/delete", URL: e2e.GetGatewayURL() + "/v1/cache/delete",
Body: map[string]interface{}{ Body: map[string]interface{}{
"dmap": dmap, "dmap": dmap,
"key": key, "key": key,

View File

@ -1,6 +1,6 @@
//go:build e2e //go:build e2e
package e2e package integration_test
import ( import (
"context" "context"
@ -12,6 +12,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/DeBrosOfficial/network/e2e"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -23,7 +24,7 @@ import (
// TestRQLite_DataPersistence verifies that RQLite data is persisted through the gateway. // TestRQLite_DataPersistence verifies that RQLite data is persisted through the gateway.
func TestRQLite_DataPersistence(t *testing.T) { func TestRQLite_DataPersistence(t *testing.T) {
SkipIfMissingGateway(t) e2e.SkipIfMissingGateway(t)
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel() defer cancel()
@ -32,18 +33,18 @@ func TestRQLite_DataPersistence(t *testing.T) {
// Cleanup // Cleanup
defer func() { defer func() {
dropReq := &HTTPRequest{ dropReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/rqlite/drop-table", URL: e2e.GetGatewayURL() + "/v1/rqlite/drop-table",
Body: map[string]interface{}{"table": tableName}, Body: map[string]interface{}{"table": tableName},
} }
dropReq.Do(context.Background()) dropReq.Do(context.Background())
}() }()
// Create table // Create table
createReq := &HTTPRequest{ createReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/rqlite/create-table", URL: e2e.GetGatewayURL() + "/v1/rqlite/create-table",
Body: map[string]interface{}{ Body: map[string]interface{}{
"schema": fmt.Sprintf( "schema": fmt.Sprintf(
"CREATE TABLE IF NOT EXISTS %s (id INTEGER PRIMARY KEY, value TEXT, version INTEGER)", "CREATE TABLE IF NOT EXISTS %s (id INTEGER PRIMARY KEY, value TEXT, version INTEGER)",
@ -65,9 +66,9 @@ func TestRQLite_DataPersistence(t *testing.T) {
fmt.Sprintf("INSERT INTO %s (value, version) VALUES ('item_%d', %d)", tableName, i, i)) fmt.Sprintf("INSERT INTO %s (value, version) VALUES ('item_%d', %d)", tableName, i, i))
} }
insertReq := &HTTPRequest{ insertReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/rqlite/transaction", URL: e2e.GetGatewayURL() + "/v1/rqlite/transaction",
Body: map[string]interface{}{"statements": statements}, Body: map[string]interface{}{"statements": statements},
} }
@ -76,9 +77,9 @@ func TestRQLite_DataPersistence(t *testing.T) {
require.Equal(t, http.StatusOK, status, "FAIL: Insert returned status %d", status) require.Equal(t, http.StatusOK, status, "FAIL: Insert returned status %d", status)
// Verify all data exists // Verify all data exists
queryReq := &HTTPRequest{ queryReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/rqlite/query", URL: e2e.GetGatewayURL() + "/v1/rqlite/query",
Body: map[string]interface{}{ Body: map[string]interface{}{
"sql": fmt.Sprintf("SELECT COUNT(*) FROM %s", tableName), "sql": fmt.Sprintf("SELECT COUNT(*) FROM %s", tableName),
}, },
@ -89,7 +90,7 @@ func TestRQLite_DataPersistence(t *testing.T) {
require.Equal(t, http.StatusOK, status, "FAIL: Count query returned status %d", status) require.Equal(t, http.StatusOK, status, "FAIL: Count query returned status %d", status)
var queryResp map[string]interface{} var queryResp map[string]interface{}
DecodeJSON(body, &queryResp) e2e.DecodeJSON(body, &queryResp)
if rows, ok := queryResp["rows"].([]interface{}); ok && len(rows) > 0 { if rows, ok := queryResp["rows"].([]interface{}); ok && len(rows) > 0 {
row := rows[0].([]interface{}) row := rows[0].([]interface{})
@ -98,9 +99,9 @@ func TestRQLite_DataPersistence(t *testing.T) {
} }
// Update data // Update data
updateReq := &HTTPRequest{ updateReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/rqlite/transaction", URL: e2e.GetGatewayURL() + "/v1/rqlite/transaction",
Body: map[string]interface{}{ Body: map[string]interface{}{
"statements": []string{ "statements": []string{
fmt.Sprintf("UPDATE %s SET version = version + 100 WHERE version <= 5", tableName), fmt.Sprintf("UPDATE %s SET version = version + 100 WHERE version <= 5", tableName),
@ -113,9 +114,9 @@ func TestRQLite_DataPersistence(t *testing.T) {
require.Equal(t, http.StatusOK, status, "FAIL: Update returned status %d", status) require.Equal(t, http.StatusOK, status, "FAIL: Update returned status %d", status)
// Verify updates persisted // Verify updates persisted
queryUpdatedReq := &HTTPRequest{ queryUpdatedReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/rqlite/query", URL: e2e.GetGatewayURL() + "/v1/rqlite/query",
Body: map[string]interface{}{ Body: map[string]interface{}{
"sql": fmt.Sprintf("SELECT COUNT(*) FROM %s WHERE version > 100", tableName), "sql": fmt.Sprintf("SELECT COUNT(*) FROM %s WHERE version > 100", tableName),
}, },
@ -125,7 +126,7 @@ func TestRQLite_DataPersistence(t *testing.T) {
require.NoError(t, err, "FAIL: Could not count updated rows") require.NoError(t, err, "FAIL: Could not count updated rows")
require.Equal(t, http.StatusOK, status, "FAIL: Count updated query returned status %d", status) require.Equal(t, http.StatusOK, status, "FAIL: Count updated query returned status %d", status)
DecodeJSON(body, &queryResp) e2e.DecodeJSON(body, &queryResp)
if rows, ok := queryResp["rows"].([]interface{}); ok && len(rows) > 0 { if rows, ok := queryResp["rows"].([]interface{}); ok && len(rows) > 0 {
row := rows[0].([]interface{}) row := rows[0].([]interface{})
count := int(row[0].(float64)) count := int(row[0].(float64))
@ -137,9 +138,9 @@ func TestRQLite_DataPersistence(t *testing.T) {
t.Run("Deletes_are_persisted", func(t *testing.T) { t.Run("Deletes_are_persisted", func(t *testing.T) {
// Delete some rows // Delete some rows
deleteReq := &HTTPRequest{ deleteReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/rqlite/transaction", URL: e2e.GetGatewayURL() + "/v1/rqlite/transaction",
Body: map[string]interface{}{ Body: map[string]interface{}{
"statements": []string{ "statements": []string{
fmt.Sprintf("DELETE FROM %s WHERE version > 100", tableName), fmt.Sprintf("DELETE FROM %s WHERE version > 100", tableName),
@ -152,9 +153,9 @@ func TestRQLite_DataPersistence(t *testing.T) {
require.Equal(t, http.StatusOK, status, "FAIL: Delete returned status %d", status) require.Equal(t, http.StatusOK, status, "FAIL: Delete returned status %d", status)
// Verify deletes persisted // Verify deletes persisted
queryReq := &HTTPRequest{ queryReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/rqlite/query", URL: e2e.GetGatewayURL() + "/v1/rqlite/query",
Body: map[string]interface{}{ Body: map[string]interface{}{
"sql": fmt.Sprintf("SELECT COUNT(*) FROM %s", tableName), "sql": fmt.Sprintf("SELECT COUNT(*) FROM %s", tableName),
}, },
@ -165,7 +166,7 @@ func TestRQLite_DataPersistence(t *testing.T) {
require.Equal(t, http.StatusOK, status, "FAIL: Count query returned status %d", status) require.Equal(t, http.StatusOK, status, "FAIL: Count query returned status %d", status)
var queryResp map[string]interface{} var queryResp map[string]interface{}
DecodeJSON(body, &queryResp) e2e.DecodeJSON(body, &queryResp)
if rows, ok := queryResp["rows"].([]interface{}); ok && len(rows) > 0 { if rows, ok := queryResp["rows"].([]interface{}); ok && len(rows) > 0 {
row := rows[0].([]interface{}) row := rows[0].([]interface{})
@ -213,7 +214,7 @@ func TestRQLite_DataFilesExist(t *testing.T) {
// TestOlric_DataPersistence verifies Olric cache data persistence. // TestOlric_DataPersistence verifies Olric cache data persistence.
// Note: Olric is an in-memory cache, so this tests data survival during runtime. // Note: Olric is an in-memory cache, so this tests data survival during runtime.
func TestOlric_DataPersistence(t *testing.T) { func TestOlric_DataPersistence(t *testing.T) {
env, err := LoadTestEnv() env, err := e2e.LoadTestEnv()
require.NoError(t, err, "FAIL: Could not load test environment") require.NoError(t, err, "FAIL: Could not load test environment")
dmap := fmt.Sprintf("persist_cache_%d", time.Now().UnixNano()) dmap := fmt.Sprintf("persist_cache_%d", time.Now().UnixNano())
@ -226,17 +227,17 @@ func TestOlric_DataPersistence(t *testing.T) {
value := fmt.Sprintf("persist_value_%d", i) value := fmt.Sprintf("persist_value_%d", i)
keys[key] = value keys[key] = value
err := putToOlric(env.GatewayURL, env.APIKey, dmap, key, value) err := e2e.PutToOlric(env.GatewayURL, env.APIKey, dmap, key, value)
require.NoError(t, err, "FAIL: Could not put key %s", key) require.NoError(t, err, "FAIL: Could not put key %s", key)
} }
// Perform other operations // Perform other operations
err := putToOlric(env.GatewayURL, env.APIKey, dmap, "other_key", "other_value") err := e2e.PutToOlric(env.GatewayURL, env.APIKey, dmap, "other_key", "other_value")
require.NoError(t, err, "FAIL: Could not put other key") require.NoError(t, err, "FAIL: Could not put other key")
// Verify original keys still exist // Verify original keys still exist
for key, expectedValue := range keys { for key, expectedValue := range keys {
retrieved, err := getFromOlric(env.GatewayURL, env.APIKey, dmap, key) retrieved, err := e2e.GetFromOlric(env.GatewayURL, env.APIKey, dmap, key)
require.NoError(t, err, "FAIL: Key %s not found after other operations", key) require.NoError(t, err, "FAIL: Key %s not found after other operations", key)
require.Equal(t, expectedValue, retrieved, "FAIL: Value mismatch for key %s", key) require.Equal(t, expectedValue, retrieved, "FAIL: Value mismatch for key %s", key)
} }
@ -249,7 +250,7 @@ func TestOlric_DataPersistence(t *testing.T) {
func TestNamespaceCluster_DataPersistence(t *testing.T) { func TestNamespaceCluster_DataPersistence(t *testing.T) {
// Create namespace // Create namespace
namespace := fmt.Sprintf("persist-ns-%d", time.Now().UnixNano()) namespace := fmt.Sprintf("persist-ns-%d", time.Now().UnixNano())
env, err := LoadTestEnvWithNamespace(namespace) env, err := e2e.LoadTestEnvWithNamespace(namespace)
require.NoError(t, err, "FAIL: Could not create namespace") require.NoError(t, err, "FAIL: Could not create namespace")
t.Logf("Created namespace: %s", namespace) t.Logf("Created namespace: %s", namespace)
@ -261,7 +262,7 @@ func TestNamespaceCluster_DataPersistence(t *testing.T) {
// Create data via gateway API // Create data via gateway API
tableName := fmt.Sprintf("ns_data_%d", time.Now().UnixNano()) tableName := fmt.Sprintf("ns_data_%d", time.Now().UnixNano())
req := &HTTPRequest{ req := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: env.GatewayURL + "/v1/rqlite/create-table", URL: env.GatewayURL + "/v1/rqlite/create-table",
Headers: map[string]string{ Headers: map[string]string{
@ -278,7 +279,7 @@ func TestNamespaceCluster_DataPersistence(t *testing.T) {
"FAIL: Create table returned status %d", status) "FAIL: Create table returned status %d", status)
// Insert data // Insert data
insertReq := &HTTPRequest{ insertReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: env.GatewayURL + "/v1/rqlite/transaction", URL: env.GatewayURL + "/v1/rqlite/transaction",
Headers: map[string]string{ Headers: map[string]string{
@ -296,7 +297,7 @@ func TestNamespaceCluster_DataPersistence(t *testing.T) {
require.Equal(t, http.StatusOK, status, "FAIL: Insert returned status %d", status) require.Equal(t, http.StatusOK, status, "FAIL: Insert returned status %d", status)
// Verify data exists // Verify data exists
queryReq := &HTTPRequest{ queryReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: env.GatewayURL + "/v1/rqlite/query", URL: env.GatewayURL + "/v1/rqlite/query",
Headers: map[string]string{ Headers: map[string]string{
@ -323,7 +324,7 @@ func TestNamespaceCluster_DataPersistence(t *testing.T) {
// TestIPFS_DataPersistence verifies IPFS content is persisted and pinned. // TestIPFS_DataPersistence verifies IPFS content is persisted and pinned.
// Note: Detailed IPFS tests are in storage_http_test.go. This test uses the helper from env.go. // Note: Detailed IPFS tests are in storage_http_test.go. This test uses the helper from env.go.
func TestIPFS_DataPersistence(t *testing.T) { func TestIPFS_DataPersistence(t *testing.T) {
env, err := LoadTestEnv() env, err := e2e.LoadTestEnv()
require.NoError(t, err, "FAIL: Could not load test environment") require.NoError(t, err, "FAIL: Could not load test environment")
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
@ -332,12 +333,12 @@ func TestIPFS_DataPersistence(t *testing.T) {
t.Run("Uploaded_content_persists", func(t *testing.T) { t.Run("Uploaded_content_persists", func(t *testing.T) {
// Use helper function to upload content via multipart form // Use helper function to upload content via multipart form
content := fmt.Sprintf("persistent content %d", time.Now().UnixNano()) content := fmt.Sprintf("persistent content %d", time.Now().UnixNano())
cid := UploadTestFile(t, env, "persist_test.txt", content) cid := e2e.UploadTestFile(t, env, "persist_test.txt", content)
require.NotEmpty(t, cid, "FAIL: No CID returned from upload") require.NotEmpty(t, cid, "FAIL: No CID returned from upload")
t.Logf(" Uploaded content with CID: %s", cid) t.Logf(" Uploaded content with CID: %s", cid)
// Verify content can be retrieved // Verify content can be retrieved
getReq := &HTTPRequest{ getReq := &e2e.HTTPRequest{
Method: http.MethodGet, Method: http.MethodGet,
URL: env.GatewayURL + "/v1/storage/get/" + cid, URL: env.GatewayURL + "/v1/storage/get/" + cid,
Headers: map[string]string{ Headers: map[string]string{
@ -357,7 +358,7 @@ func TestIPFS_DataPersistence(t *testing.T) {
// TestSQLite_DataPersistence verifies per-deployment SQLite databases persist. // TestSQLite_DataPersistence verifies per-deployment SQLite databases persist.
func TestSQLite_DataPersistence(t *testing.T) { func TestSQLite_DataPersistence(t *testing.T) {
env, err := LoadTestEnv() env, err := e2e.LoadTestEnv()
require.NoError(t, err, "FAIL: Could not load test environment") require.NoError(t, err, "FAIL: Could not load test environment")
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
@ -367,7 +368,7 @@ func TestSQLite_DataPersistence(t *testing.T) {
t.Run("SQLite_database_persists", func(t *testing.T) { t.Run("SQLite_database_persists", func(t *testing.T) {
// Create database // Create database
createReq := &HTTPRequest{ createReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: env.GatewayURL + "/v1/db/sqlite/create", URL: env.GatewayURL + "/v1/db/sqlite/create",
Headers: map[string]string{ Headers: map[string]string{
@ -385,7 +386,7 @@ func TestSQLite_DataPersistence(t *testing.T) {
t.Logf(" Created SQLite database: %s", dbName) t.Logf(" Created SQLite database: %s", dbName)
// Create table and insert data // Create table and insert data
queryReq := &HTTPRequest{ queryReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: env.GatewayURL + "/v1/db/sqlite/query", URL: env.GatewayURL + "/v1/db/sqlite/query",
Headers: map[string]string{ Headers: map[string]string{
@ -402,7 +403,7 @@ func TestSQLite_DataPersistence(t *testing.T) {
require.Equal(t, http.StatusOK, status, "FAIL: Create table returned status %d", status) require.Equal(t, http.StatusOK, status, "FAIL: Create table returned status %d", status)
// Insert data // Insert data
insertReq := &HTTPRequest{ insertReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: env.GatewayURL + "/v1/db/sqlite/query", URL: env.GatewayURL + "/v1/db/sqlite/query",
Headers: map[string]string{ Headers: map[string]string{
@ -419,7 +420,7 @@ func TestSQLite_DataPersistence(t *testing.T) {
require.Equal(t, http.StatusOK, status, "FAIL: Insert returned status %d", status) require.Equal(t, http.StatusOK, status, "FAIL: Insert returned status %d", status)
// Verify data persists // Verify data persists
selectReq := &HTTPRequest{ selectReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: env.GatewayURL + "/v1/db/sqlite/query", URL: env.GatewayURL + "/v1/db/sqlite/query",
Headers: map[string]string{ Headers: map[string]string{
@ -442,7 +443,7 @@ func TestSQLite_DataPersistence(t *testing.T) {
t.Run("SQLite_database_listed", func(t *testing.T) { t.Run("SQLite_database_listed", func(t *testing.T) {
// List databases to verify it was persisted // List databases to verify it was persisted
listReq := &HTTPRequest{ listReq := &e2e.HTTPRequest{
Method: http.MethodGet, Method: http.MethodGet,
URL: env.GatewayURL + "/v1/db/sqlite/list", URL: env.GatewayURL + "/v1/db/sqlite/list",
Headers: map[string]string{ Headers: map[string]string{

View File

@ -1,6 +1,6 @@
//go:build e2e //go:build e2e
package e2e package integration_test
import ( import (
"encoding/json" "encoding/json"
@ -12,21 +12,22 @@ import (
"testing" "testing"
"time" "time"
"github.com/DeBrosOfficial/network/e2e"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func TestDomainRouting_BasicRouting(t *testing.T) { func TestDomainRouting_BasicRouting(t *testing.T) {
env, err := LoadTestEnv() env, err := e2e.LoadTestEnv()
require.NoError(t, err, "Failed to load test environment") require.NoError(t, err, "Failed to load test environment")
deploymentName := fmt.Sprintf("test-routing-%d", time.Now().Unix()) deploymentName := fmt.Sprintf("test-routing-%d", time.Now().Unix())
tarballPath := filepath.Join("../testdata/tarballs/react-vite.tar.gz") tarballPath := filepath.Join("../testdata/tarballs/react-vite.tar.gz")
deploymentID := CreateTestDeployment(t, env, deploymentName, tarballPath) deploymentID := e2e.CreateTestDeployment(t, env, deploymentName, tarballPath)
defer func() { defer func() {
if !env.SkipCleanup { if !env.SkipCleanup {
DeleteDeployment(t, env, deploymentID) e2e.DeleteDeployment(t, env, deploymentID)
} }
}() }()
@ -34,7 +35,7 @@ func TestDomainRouting_BasicRouting(t *testing.T) {
time.Sleep(2 * time.Second) time.Sleep(2 * time.Second)
// Get deployment details for debugging // Get deployment details for debugging
deployment := GetDeployment(t, env, deploymentID) deployment := e2e.GetDeployment(t, env, deploymentID)
t.Logf("Deployment created: ID=%s, CID=%s, Name=%s, Status=%s", t.Logf("Deployment created: ID=%s, CID=%s, Name=%s, Status=%s",
deploymentID, deployment["content_cid"], deployment["name"], deployment["status"]) deploymentID, deployment["content_cid"], deployment["name"], deployment["status"])
@ -42,7 +43,7 @@ func TestDomainRouting_BasicRouting(t *testing.T) {
// Domain format: {deploymentName}.{baseDomain} // Domain format: {deploymentName}.{baseDomain}
domain := env.BuildDeploymentDomain(deploymentName) domain := env.BuildDeploymentDomain(deploymentName)
resp := TestDeploymentWithHostHeader(t, env, domain, "/") resp := e2e.TestDeploymentWithHostHeader(t, env, domain, "/")
defer resp.Body.Close() defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode, "Should return 200 OK") assert.Equal(t, http.StatusOK, resp.StatusCode, "Should return 200 OK")
@ -58,7 +59,7 @@ func TestDomainRouting_BasicRouting(t *testing.T) {
t.Run("Non-debros domain passes through", func(t *testing.T) { t.Run("Non-debros domain passes through", func(t *testing.T) {
// Request with non-debros domain should not route to deployment // Request with non-debros domain should not route to deployment
resp := TestDeploymentWithHostHeader(t, env, "example.com", "/") resp := e2e.TestDeploymentWithHostHeader(t, env, "example.com", "/")
defer resp.Body.Close() defer resp.Body.Close()
// Should either return 404 or pass to default handler // Should either return 404 or pass to default handler
@ -98,7 +99,7 @@ func TestDomainRouting_BasicRouting(t *testing.T) {
domain := env.BuildDeploymentDomain(deploymentName) domain := env.BuildDeploymentDomain(deploymentName)
// /.well-known/ paths should bypass (used for ACME challenges, etc.) // /.well-known/ paths should bypass (used for ACME challenges, etc.)
resp := TestDeploymentWithHostHeader(t, env, domain, "/.well-known/acme-challenge/test") resp := e2e.TestDeploymentWithHostHeader(t, env, domain, "/.well-known/acme-challenge/test")
defer resp.Body.Close() defer resp.Body.Close()
// Should not serve deployment content // Should not serve deployment content
@ -117,7 +118,7 @@ func TestDomainRouting_BasicRouting(t *testing.T) {
} }
func TestDomainRouting_MultipleDeployments(t *testing.T) { func TestDomainRouting_MultipleDeployments(t *testing.T) {
env, err := LoadTestEnv() env, err := e2e.LoadTestEnv()
require.NoError(t, err, "Failed to load test environment") require.NoError(t, err, "Failed to load test environment")
tarballPath := filepath.Join("../testdata/tarballs/react-vite.tar.gz") tarballPath := filepath.Join("../testdata/tarballs/react-vite.tar.gz")
@ -126,14 +127,14 @@ func TestDomainRouting_MultipleDeployments(t *testing.T) {
deployment1Name := fmt.Sprintf("test-multi-1-%d", time.Now().Unix()) deployment1Name := fmt.Sprintf("test-multi-1-%d", time.Now().Unix())
deployment2Name := fmt.Sprintf("test-multi-2-%d", time.Now().Unix()) deployment2Name := fmt.Sprintf("test-multi-2-%d", time.Now().Unix())
deployment1ID := CreateTestDeployment(t, env, deployment1Name, tarballPath) deployment1ID := e2e.CreateTestDeployment(t, env, deployment1Name, tarballPath)
time.Sleep(1 * time.Second) time.Sleep(1 * time.Second)
deployment2ID := CreateTestDeployment(t, env, deployment2Name, tarballPath) deployment2ID := e2e.CreateTestDeployment(t, env, deployment2Name, tarballPath)
defer func() { defer func() {
if !env.SkipCleanup { if !env.SkipCleanup {
DeleteDeployment(t, env, deployment1ID) e2e.DeleteDeployment(t, env, deployment1ID)
DeleteDeployment(t, env, deployment2ID) e2e.DeleteDeployment(t, env, deployment2ID)
} }
}() }()
@ -144,13 +145,13 @@ func TestDomainRouting_MultipleDeployments(t *testing.T) {
domain2 := env.BuildDeploymentDomain(deployment2Name) domain2 := env.BuildDeploymentDomain(deployment2Name)
// Test deployment 1 // Test deployment 1
resp1 := TestDeploymentWithHostHeader(t, env, domain1, "/") resp1 := e2e.TestDeploymentWithHostHeader(t, env, domain1, "/")
defer resp1.Body.Close() defer resp1.Body.Close()
assert.Equal(t, http.StatusOK, resp1.StatusCode, "Deployment 1 should serve") assert.Equal(t, http.StatusOK, resp1.StatusCode, "Deployment 1 should serve")
// Test deployment 2 // Test deployment 2
resp2 := TestDeploymentWithHostHeader(t, env, domain2, "/") resp2 := e2e.TestDeploymentWithHostHeader(t, env, domain2, "/")
defer resp2.Body.Close() defer resp2.Body.Close()
assert.Equal(t, http.StatusOK, resp2.StatusCode, "Deployment 2 should serve") assert.Equal(t, http.StatusOK, resp2.StatusCode, "Deployment 2 should serve")
@ -164,7 +165,7 @@ func TestDomainRouting_MultipleDeployments(t *testing.T) {
// Request with non-existent deployment subdomain // Request with non-existent deployment subdomain
fakeDeploymentDomain := env.BuildDeploymentDomain(fmt.Sprintf("nonexistent-deployment-%d", time.Now().Unix())) fakeDeploymentDomain := env.BuildDeploymentDomain(fmt.Sprintf("nonexistent-deployment-%d", time.Now().Unix()))
resp := TestDeploymentWithHostHeader(t, env, fakeDeploymentDomain, "/") resp := e2e.TestDeploymentWithHostHeader(t, env, fakeDeploymentDomain, "/")
defer resp.Body.Close() defer resp.Body.Close()
assert.Equal(t, http.StatusNotFound, resp.StatusCode, assert.Equal(t, http.StatusNotFound, resp.StatusCode,
@ -175,16 +176,16 @@ func TestDomainRouting_MultipleDeployments(t *testing.T) {
} }
func TestDomainRouting_ContentTypes(t *testing.T) { func TestDomainRouting_ContentTypes(t *testing.T) {
env, err := LoadTestEnv() env, err := e2e.LoadTestEnv()
require.NoError(t, err, "Failed to load test environment") require.NoError(t, err, "Failed to load test environment")
deploymentName := fmt.Sprintf("test-content-types-%d", time.Now().Unix()) deploymentName := fmt.Sprintf("test-content-types-%d", time.Now().Unix())
tarballPath := filepath.Join("../testdata/tarballs/react-vite.tar.gz") tarballPath := filepath.Join("../testdata/tarballs/react-vite.tar.gz")
deploymentID := CreateTestDeployment(t, env, deploymentName, tarballPath) deploymentID := e2e.CreateTestDeployment(t, env, deploymentName, tarballPath)
defer func() { defer func() {
if !env.SkipCleanup { if !env.SkipCleanup {
DeleteDeployment(t, env, deploymentID) e2e.DeleteDeployment(t, env, deploymentID)
} }
}() }()
@ -203,7 +204,7 @@ func TestDomainRouting_ContentTypes(t *testing.T) {
for _, test := range contentTypeTests { for _, test := range contentTypeTests {
t.Run(test.description, func(t *testing.T) { t.Run(test.description, func(t *testing.T) {
resp := TestDeploymentWithHostHeader(t, env, domain, test.path) resp := e2e.TestDeploymentWithHostHeader(t, env, domain, test.path)
defer resp.Body.Close() defer resp.Body.Close()
if resp.StatusCode == http.StatusOK { if resp.StatusCode == http.StatusOK {
@ -220,16 +221,16 @@ func TestDomainRouting_ContentTypes(t *testing.T) {
} }
func TestDomainRouting_SPAFallback(t *testing.T) { func TestDomainRouting_SPAFallback(t *testing.T) {
env, err := LoadTestEnv() env, err := e2e.LoadTestEnv()
require.NoError(t, err, "Failed to load test environment") require.NoError(t, err, "Failed to load test environment")
deploymentName := fmt.Sprintf("test-spa-%d", time.Now().Unix()) deploymentName := fmt.Sprintf("test-spa-%d", time.Now().Unix())
tarballPath := filepath.Join("../testdata/tarballs/react-vite.tar.gz") tarballPath := filepath.Join("../testdata/tarballs/react-vite.tar.gz")
deploymentID := CreateTestDeployment(t, env, deploymentName, tarballPath) deploymentID := e2e.CreateTestDeployment(t, env, deploymentName, tarballPath)
defer func() { defer func() {
if !env.SkipCleanup { if !env.SkipCleanup {
DeleteDeployment(t, env, deploymentID) e2e.DeleteDeployment(t, env, deploymentID)
} }
}() }()
@ -246,7 +247,7 @@ func TestDomainRouting_SPAFallback(t *testing.T) {
} }
for _, path := range unknownPaths { for _, path := range unknownPaths {
resp := TestDeploymentWithHostHeader(t, env, domain, path) resp := e2e.TestDeploymentWithHostHeader(t, env, domain, path)
body, _ := io.ReadAll(resp.Body) body, _ := io.ReadAll(resp.Body)
resp.Body.Close() resp.Body.Close()
@ -266,16 +267,16 @@ func TestDomainRouting_SPAFallback(t *testing.T) {
// - CORRECT: {name}-{random}.{baseDomain} (e.g., "myapp-f3o4if.dbrs.space") // - CORRECT: {name}-{random}.{baseDomain} (e.g., "myapp-f3o4if.dbrs.space")
// - WRONG: {name}.node-{shortID}.{baseDomain} (should NOT exist) // - WRONG: {name}.node-{shortID}.{baseDomain} (should NOT exist)
func TestDeployment_DomainFormat(t *testing.T) { func TestDeployment_DomainFormat(t *testing.T) {
env, err := LoadTestEnv() env, err := e2e.LoadTestEnv()
require.NoError(t, err, "Failed to load test environment") require.NoError(t, err, "Failed to load test environment")
deploymentName := fmt.Sprintf("format-test-%d", time.Now().Unix()) deploymentName := fmt.Sprintf("format-test-%d", time.Now().Unix())
tarballPath := filepath.Join("../testdata/tarballs/react-vite.tar.gz") tarballPath := filepath.Join("../testdata/tarballs/react-vite.tar.gz")
deploymentID := CreateTestDeployment(t, env, deploymentName, tarballPath) deploymentID := e2e.CreateTestDeployment(t, env, deploymentName, tarballPath)
defer func() { defer func() {
if !env.SkipCleanup { if !env.SkipCleanup {
DeleteDeployment(t, env, deploymentID) e2e.DeleteDeployment(t, env, deploymentID)
} }
}() }()
@ -283,7 +284,7 @@ func TestDeployment_DomainFormat(t *testing.T) {
time.Sleep(2 * time.Second) time.Sleep(2 * time.Second)
t.Run("Deployment URL has correct format", func(t *testing.T) { t.Run("Deployment URL has correct format", func(t *testing.T) {
deployment := GetDeployment(t, env, deploymentID) deployment := e2e.GetDeployment(t, env, deploymentID)
// Get the deployment URLs // Get the deployment URLs
urls, ok := deployment["urls"].([]interface{}) urls, ok := deployment["urls"].([]interface{})
@ -331,14 +332,14 @@ func TestDeployment_DomainFormat(t *testing.T) {
t.Run("Domain resolves via Host header", func(t *testing.T) { t.Run("Domain resolves via Host header", func(t *testing.T) {
// Get the actual subdomain from the deployment // Get the actual subdomain from the deployment
deployment := GetDeployment(t, env, deploymentID) deployment := e2e.GetDeployment(t, env, deploymentID)
subdomain, _ := deployment["subdomain"].(string) subdomain, _ := deployment["subdomain"].(string)
if subdomain == "" { if subdomain == "" {
t.Skip("No subdomain set, skipping host header test") t.Skip("No subdomain set, skipping host header test")
} }
domain := subdomain + "." + env.BaseDomain domain := subdomain + "." + env.BaseDomain
resp := TestDeploymentWithHostHeader(t, env, domain, "/") resp := e2e.TestDeploymentWithHostHeader(t, env, domain, "/")
defer resp.Body.Close() defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode, assert.Equal(t, http.StatusOK, resp.StatusCode,

View File

@ -1,6 +1,6 @@
//go:build e2e //go:build e2e
package e2e package integration_test
import ( import (
"bytes" "bytes"
@ -12,12 +12,13 @@ import (
"testing" "testing"
"time" "time"
"github.com/DeBrosOfficial/network/e2e"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func TestFullStack_GoAPI_SQLite(t *testing.T) { func TestFullStack_GoAPI_SQLite(t *testing.T) {
env, err := LoadTestEnv() env, err := e2e.LoadTestEnv()
require.NoError(t, err, "Failed to load test environment") require.NoError(t, err, "Failed to load test environment")
appName := fmt.Sprintf("fullstack-app-%d", time.Now().Unix()) appName := fmt.Sprintf("fullstack-app-%d", time.Now().Unix())
@ -29,15 +30,15 @@ func TestFullStack_GoAPI_SQLite(t *testing.T) {
defer func() { defer func() {
if !env.SkipCleanup { if !env.SkipCleanup {
if backendID != "" { if backendID != "" {
DeleteDeployment(t, env, backendID) e2e.DeleteDeployment(t, env, backendID)
} }
DeleteSQLiteDB(t, env, dbName) e2e.DeleteSQLiteDB(t, env, dbName)
} }
}() }()
// Step 1: Create SQLite database // Step 1: Create SQLite database
t.Run("Create SQLite database", func(t *testing.T) { t.Run("Create SQLite database", func(t *testing.T) {
CreateSQLiteDB(t, env, dbName) e2e.CreateSQLiteDB(t, env, dbName)
// Create users table // Create users table
query := `CREATE TABLE users ( query := `CREATE TABLE users (
@ -46,11 +47,11 @@ func TestFullStack_GoAPI_SQLite(t *testing.T) {
email TEXT UNIQUE NOT NULL, email TEXT UNIQUE NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)` )`
ExecuteSQLQuery(t, env, dbName, query) e2e.ExecuteSQLQuery(t, env, dbName, query)
// Insert test data // Insert test data
insertQuery := `INSERT INTO users (name, email) VALUES ('Alice', 'alice@example.com')` insertQuery := `INSERT INTO users (name, email) VALUES ('Alice', 'alice@example.com')`
result := ExecuteSQLQuery(t, env, dbName, insertQuery) result := e2e.ExecuteSQLQuery(t, env, dbName, insertQuery)
assert.NotNil(t, result, "Should execute INSERT successfully") assert.NotNil(t, result, "Should execute INSERT successfully")
t.Logf("✓ Database created with users table") t.Logf("✓ Database created with users table")
@ -64,7 +65,7 @@ func TestFullStack_GoAPI_SQLite(t *testing.T) {
// Note: In a real implementation, we would pass DATABASE_NAME env var // Note: In a real implementation, we would pass DATABASE_NAME env var
// For now, we just test the deployment mechanism // For now, we just test the deployment mechanism
backendID = CreateTestDeployment(t, env, backendName, tarballPath) backendID = e2e.CreateTestDeployment(t, env, backendName, tarballPath)
assert.NotEmpty(t, backendID, "Backend deployment ID should not be empty") assert.NotEmpty(t, backendID, "Backend deployment ID should not be empty")
t.Logf("✓ Go backend deployed: %s", backendName) t.Logf("✓ Go backend deployed: %s", backendName)
@ -77,10 +78,10 @@ func TestFullStack_GoAPI_SQLite(t *testing.T) {
t.Run("Test database CRUD operations", func(t *testing.T) { t.Run("Test database CRUD operations", func(t *testing.T) {
// INSERT // INSERT
insertQuery := `INSERT INTO users (name, email) VALUES ('Bob', 'bob@example.com')` insertQuery := `INSERT INTO users (name, email) VALUES ('Bob', 'bob@example.com')`
ExecuteSQLQuery(t, env, dbName, insertQuery) e2e.ExecuteSQLQuery(t, env, dbName, insertQuery)
// SELECT // SELECT
users := QuerySQLite(t, env, dbName, "SELECT * FROM users ORDER BY id") users := e2e.QuerySQLite(t, env, dbName, "SELECT * FROM users ORDER BY id")
require.GreaterOrEqual(t, len(users), 2, "Should have at least 2 users") require.GreaterOrEqual(t, len(users), 2, "Should have at least 2 users")
assert.Equal(t, "Alice", users[0]["name"], "First user should be Alice") assert.Equal(t, "Alice", users[0]["name"], "First user should be Alice")
@ -91,14 +92,14 @@ func TestFullStack_GoAPI_SQLite(t *testing.T) {
// UPDATE // UPDATE
updateQuery := `UPDATE users SET email = 'alice.new@example.com' WHERE name = 'Alice'` updateQuery := `UPDATE users SET email = 'alice.new@example.com' WHERE name = 'Alice'`
result := ExecuteSQLQuery(t, env, dbName, updateQuery) result := e2e.ExecuteSQLQuery(t, env, dbName, updateQuery)
rowsAffected, ok := result["rows_affected"].(float64) rowsAffected, ok := result["rows_affected"].(float64)
require.True(t, ok, "Should have rows_affected") require.True(t, ok, "Should have rows_affected")
assert.Equal(t, float64(1), rowsAffected, "Should update 1 row") assert.Equal(t, float64(1), rowsAffected, "Should update 1 row")
// Verify update // Verify update
updated := QuerySQLite(t, env, dbName, "SELECT email FROM users WHERE name = 'Alice'") updated := e2e.QuerySQLite(t, env, dbName, "SELECT email FROM users WHERE name = 'Alice'")
require.Len(t, updated, 1, "Should find Alice") require.Len(t, updated, 1, "Should find Alice")
assert.Equal(t, "alice.new@example.com", updated[0]["email"], "Email should be updated") assert.Equal(t, "alice.new@example.com", updated[0]["email"], "Email should be updated")
@ -106,14 +107,14 @@ func TestFullStack_GoAPI_SQLite(t *testing.T) {
// DELETE // DELETE
deleteQuery := `DELETE FROM users WHERE name = 'Bob'` deleteQuery := `DELETE FROM users WHERE name = 'Bob'`
result = ExecuteSQLQuery(t, env, dbName, deleteQuery) result = e2e.ExecuteSQLQuery(t, env, dbName, deleteQuery)
rowsAffected, ok = result["rows_affected"].(float64) rowsAffected, ok = result["rows_affected"].(float64)
require.True(t, ok, "Should have rows_affected") require.True(t, ok, "Should have rows_affected")
assert.Equal(t, float64(1), rowsAffected, "Should delete 1 row") assert.Equal(t, float64(1), rowsAffected, "Should delete 1 row")
// Verify deletion // Verify deletion
remaining := QuerySQLite(t, env, dbName, "SELECT * FROM users") remaining := e2e.QuerySQLite(t, env, dbName, "SELECT * FROM users")
assert.Equal(t, 1, len(remaining), "Should have 1 user remaining") assert.Equal(t, 1, len(remaining), "Should have 1 user remaining")
t.Logf("✓ DELETE operation verified") t.Logf("✓ DELETE operation verified")
@ -121,7 +122,7 @@ func TestFullStack_GoAPI_SQLite(t *testing.T) {
// Step 4: Test backend API endpoints (if deployment is active) // Step 4: Test backend API endpoints (if deployment is active)
t.Run("Test backend API endpoints", func(t *testing.T) { t.Run("Test backend API endpoints", func(t *testing.T) {
deployment := GetDeployment(t, env, backendID) deployment := e2e.GetDeployment(t, env, backendID)
status, ok := deployment["status"].(string) status, ok := deployment["status"].(string)
if !ok || status != "active" { if !ok || status != "active" {
@ -132,7 +133,7 @@ func TestFullStack_GoAPI_SQLite(t *testing.T) {
backendDomain := env.BuildDeploymentDomain(backendName) backendDomain := env.BuildDeploymentDomain(backendName)
// Test health endpoint // Test health endpoint
resp := TestDeploymentWithHostHeader(t, env, backendDomain, "/health") resp := e2e.TestDeploymentWithHostHeader(t, env, backendDomain, "/health")
defer resp.Body.Close() defer resp.Body.Close()
if resp.StatusCode == http.StatusOK { if resp.StatusCode == http.StatusOK {
@ -149,7 +150,7 @@ func TestFullStack_GoAPI_SQLite(t *testing.T) {
} }
// Test users API endpoint // Test users API endpoint
resp2 := TestDeploymentWithHostHeader(t, env, backendDomain, "/api/users") resp2 := e2e.TestDeploymentWithHostHeader(t, env, backendDomain, "/api/users")
defer resp2.Body.Close() defer resp2.Body.Close()
if resp2.StatusCode == http.StatusOK { if resp2.StatusCode == http.StatusOK {
@ -205,7 +206,7 @@ func TestFullStack_GoAPI_SQLite(t *testing.T) {
for i := 0; i < 5; i++ { for i := 0; i < 5; i++ {
go func(idx int) { go func(idx int) {
users := QuerySQLite(t, env, dbName, "SELECT * FROM users") users := e2e.QuerySQLite(t, env, dbName, "SELECT * FROM users")
assert.GreaterOrEqual(t, len(users), 0, "Should query successfully") assert.GreaterOrEqual(t, len(users), 0, "Should query successfully")
done <- true done <- true
}(i) }(i)
@ -226,7 +227,7 @@ func TestFullStack_GoAPI_SQLite(t *testing.T) {
} }
func TestFullStack_StaticSite_SQLite(t *testing.T) { func TestFullStack_StaticSite_SQLite(t *testing.T) {
env, err := LoadTestEnv() env, err := e2e.LoadTestEnv()
require.NoError(t, err, "Failed to load test environment") require.NoError(t, err, "Failed to load test environment")
appName := fmt.Sprintf("fullstack-static-%d", time.Now().Unix()) appName := fmt.Sprintf("fullstack-static-%d", time.Now().Unix())
@ -238,21 +239,21 @@ func TestFullStack_StaticSite_SQLite(t *testing.T) {
defer func() { defer func() {
if !env.SkipCleanup { if !env.SkipCleanup {
if frontendID != "" { if frontendID != "" {
DeleteDeployment(t, env, frontendID) e2e.DeleteDeployment(t, env, frontendID)
} }
DeleteSQLiteDB(t, env, dbName) e2e.DeleteSQLiteDB(t, env, dbName)
} }
}() }()
t.Run("Deploy static site and create database", func(t *testing.T) { t.Run("Deploy static site and create database", func(t *testing.T) {
// Create database // Create database
CreateSQLiteDB(t, env, dbName) e2e.CreateSQLiteDB(t, env, dbName)
ExecuteSQLQuery(t, env, dbName, "CREATE TABLE page_views (id INTEGER PRIMARY KEY, page TEXT, count INTEGER)") e2e.ExecuteSQLQuery(t, env, dbName, "CREATE TABLE page_views (id INTEGER PRIMARY KEY, page TEXT, count INTEGER)")
ExecuteSQLQuery(t, env, dbName, "INSERT INTO page_views (page, count) VALUES ('home', 0)") e2e.ExecuteSQLQuery(t, env, dbName, "INSERT INTO page_views (page, count) VALUES ('home', 0)")
// Deploy frontend // Deploy frontend
tarballPath := filepath.Join("../testdata/tarballs/react-vite.tar.gz") tarballPath := filepath.Join("../testdata/tarballs/react-vite.tar.gz")
frontendID = CreateTestDeployment(t, env, frontendName, tarballPath) frontendID = e2e.CreateTestDeployment(t, env, frontendName, tarballPath)
assert.NotEmpty(t, frontendID, "Frontend deployment should succeed") assert.NotEmpty(t, frontendID, "Frontend deployment should succeed")
t.Logf("✓ Static site deployed with SQLite backend") t.Logf("✓ Static site deployed with SQLite backend")
@ -265,7 +266,7 @@ func TestFullStack_StaticSite_SQLite(t *testing.T) {
frontendDomain := env.BuildDeploymentDomain(frontendName) frontendDomain := env.BuildDeploymentDomain(frontendName)
// Test frontend // Test frontend
resp := TestDeploymentWithHostHeader(t, env, frontendDomain, "/") resp := e2e.TestDeploymentWithHostHeader(t, env, frontendDomain, "/")
defer resp.Body.Close() defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode, "Frontend should serve") assert.Equal(t, http.StatusOK, resp.StatusCode, "Frontend should serve")
@ -274,10 +275,10 @@ func TestFullStack_StaticSite_SQLite(t *testing.T) {
assert.Contains(t, string(body), "<div id=\"root\">", "Should contain React app") assert.Contains(t, string(body), "<div id=\"root\">", "Should contain React app")
// Simulate page view tracking // Simulate page view tracking
ExecuteSQLQuery(t, env, dbName, "UPDATE page_views SET count = count + 1 WHERE page = 'home'") e2e.ExecuteSQLQuery(t, env, dbName, "UPDATE page_views SET count = count + 1 WHERE page = 'home'")
// Verify count // Verify count
views := QuerySQLite(t, env, dbName, "SELECT count FROM page_views WHERE page = 'home'") views := e2e.QuerySQLite(t, env, dbName, "SELECT count FROM page_views WHERE page = 'home'")
require.Len(t, views, 1, "Should have page view record") require.Len(t, views, 1, "Should have page view record")
count, ok := views[0]["count"].(float64) count, ok := views[0]["count"].(float64)

View File

@ -1,4 +1,4 @@
//go:build e2e //go:build e2e && production
package production package production

View File

@ -1,4 +1,4 @@
//go:build e2e //go:build e2e && production
package production package production

View File

@ -1,4 +1,4 @@
//go:build e2e //go:build e2e && production
package production package production

View File

@ -1,6 +1,6 @@
//go:build e2e //go:build e2e && production
package deployments_test package production
import ( import (
"crypto/tls" "crypto/tls"
@ -24,7 +24,7 @@ import (
// This test requires: // This test requires:
// - Orama deployed on a VPS with a real domain // - Orama deployed on a VPS with a real domain
// - DNS properly configured // - DNS properly configured
// - Run with: go test -v -tags e2e -run TestHTTPS ./e2e/deployments/... // - Run with: go test -v -tags "e2e production" -run TestHTTPS ./e2e/production/...
func TestHTTPS_ExternalAccess(t *testing.T) { func TestHTTPS_ExternalAccess(t *testing.T) {
// Skip if not configured for external testing // Skip if not configured for external testing
externalURL := os.Getenv("ORAMA_EXTERNAL_URL") externalURL := os.Getenv("ORAMA_EXTERNAL_URL")
@ -171,3 +171,34 @@ func TestHTTPS_DomainFormat(t *testing.T) {
} }
}) })
} }
func extractNodeURL(t *testing.T, deployment map[string]interface{}) string {
t.Helper()
if urls, ok := deployment["urls"].([]interface{}); ok && len(urls) > 0 {
if url, ok := urls[0].(string); ok {
return url
}
}
if urls, ok := deployment["urls"].(map[string]interface{}); ok {
if url, ok := urls["node"].(string); ok {
return url
}
}
return ""
}
func extractDomain(url string) string {
domain := url
if len(url) > 8 && url[:8] == "https://" {
domain = url[8:]
} else if len(url) > 7 && url[:7] == "http://" {
domain = url[7:]
}
if len(domain) > 0 && domain[len(domain)-1] == '/' {
domain = domain[:len(domain)-1]
}
return domain
}

View File

@ -1,4 +1,4 @@
//go:build e2e //go:build e2e && production
package production package production

View File

@ -1,6 +1,6 @@
//go:build e2e //go:build e2e
package e2e package shared_test
import ( import (
"context" "context"
@ -9,6 +9,8 @@ import (
"time" "time"
"unicode" "unicode"
e2e "github.com/DeBrosOfficial/network/e2e"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -23,10 +25,10 @@ func TestAuth_MissingAPIKey(t *testing.T) {
defer cancel() defer cancel()
// Request protected endpoint without auth headers // Request protected endpoint without auth headers
req, err := http.NewRequestWithContext(ctx, http.MethodGet, GetGatewayURL()+"/v1/cache/health", nil) req, err := http.NewRequestWithContext(ctx, http.MethodGet, e2e.GetGatewayURL()+"/v1/cache/health", nil)
require.NoError(t, err, "FAIL: Could not create request") require.NoError(t, err, "FAIL: Could not create request")
client := NewHTTPClient(30 * time.Second) client := e2e.NewHTTPClient(30 * time.Second)
resp, err := client.Do(req) resp, err := client.Do(req)
require.NoError(t, err, "FAIL: Request failed") require.NoError(t, err, "FAIL: Request failed")
defer resp.Body.Close() defer resp.Body.Close()
@ -42,12 +44,12 @@ func TestAuth_InvalidAPIKey(t *testing.T) {
defer cancel() defer cancel()
// Request with invalid API key // Request with invalid API key
req, err := http.NewRequestWithContext(ctx, http.MethodGet, GetGatewayURL()+"/v1/cache/health", nil) req, err := http.NewRequestWithContext(ctx, http.MethodGet, e2e.GetGatewayURL()+"/v1/cache/health", nil)
require.NoError(t, err, "FAIL: Could not create request") require.NoError(t, err, "FAIL: Could not create request")
req.Header.Set("Authorization", "Bearer invalid-key-xyz-123456789") req.Header.Set("Authorization", "Bearer invalid-key-xyz-123456789")
client := NewHTTPClient(30 * time.Second) client := e2e.NewHTTPClient(30 * time.Second)
resp, err := client.Do(req) resp, err := client.Do(req)
require.NoError(t, err, "FAIL: Request failed") require.NoError(t, err, "FAIL: Request failed")
defer resp.Body.Close() defer resp.Body.Close()
@ -63,9 +65,9 @@ func TestAuth_CacheWithoutAuth(t *testing.T) {
defer cancel() defer cancel()
// Request cache endpoint without auth // Request cache endpoint without auth
req := &HTTPRequest{ req := &e2e.HTTPRequest{
Method: http.MethodGet, Method: http.MethodGet,
URL: GetGatewayURL() + "/v1/cache/health", URL: e2e.GetGatewayURL() + "/v1/cache/health",
SkipAuth: true, SkipAuth: true,
} }
@ -83,9 +85,9 @@ func TestAuth_StorageWithoutAuth(t *testing.T) {
defer cancel() defer cancel()
// Request storage endpoint without auth // Request storage endpoint without auth
req := &HTTPRequest{ req := &e2e.HTTPRequest{
Method: http.MethodGet, Method: http.MethodGet,
URL: GetGatewayURL() + "/v1/storage/status/QmTest", URL: e2e.GetGatewayURL() + "/v1/storage/status/QmTest",
SkipAuth: true, SkipAuth: true,
} }
@ -103,9 +105,9 @@ func TestAuth_RQLiteWithoutAuth(t *testing.T) {
defer cancel() defer cancel()
// Request rqlite endpoint without auth // Request rqlite endpoint without auth
req := &HTTPRequest{ req := &e2e.HTTPRequest{
Method: http.MethodGet, Method: http.MethodGet,
URL: GetGatewayURL() + "/v1/rqlite/schema", URL: e2e.GetGatewayURL() + "/v1/rqlite/schema",
SkipAuth: true, SkipAuth: true,
} }
@ -123,12 +125,12 @@ func TestAuth_MalformedBearerToken(t *testing.T) {
defer cancel() defer cancel()
// Request with malformed bearer token (missing "Bearer " prefix) // Request with malformed bearer token (missing "Bearer " prefix)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, GetGatewayURL()+"/v1/cache/health", nil) req, err := http.NewRequestWithContext(ctx, http.MethodGet, e2e.GetGatewayURL()+"/v1/cache/health", nil)
require.NoError(t, err, "FAIL: Could not create request") require.NoError(t, err, "FAIL: Could not create request")
req.Header.Set("Authorization", "invalid-token-format-no-bearer") req.Header.Set("Authorization", "invalid-token-format-no-bearer")
client := NewHTTPClient(30 * time.Second) client := e2e.NewHTTPClient(30 * time.Second)
resp, err := client.Do(req) resp, err := client.Do(req)
require.NoError(t, err, "FAIL: Request failed") require.NoError(t, err, "FAIL: Request failed")
defer resp.Body.Close() defer resp.Body.Close()
@ -144,12 +146,12 @@ func TestAuth_ExpiredJWT(t *testing.T) {
defer cancel() defer cancel()
// Test with a clearly invalid JWT structure // Test with a clearly invalid JWT structure
req, err := http.NewRequestWithContext(ctx, http.MethodGet, GetGatewayURL()+"/v1/cache/health", nil) req, err := http.NewRequestWithContext(ctx, http.MethodGet, e2e.GetGatewayURL()+"/v1/cache/health", nil)
require.NoError(t, err, "FAIL: Could not create request") require.NoError(t, err, "FAIL: Could not create request")
req.Header.Set("Authorization", "Bearer expired.jwt.token.invalid") req.Header.Set("Authorization", "Bearer expired.jwt.token.invalid")
client := NewHTTPClient(30 * time.Second) client := e2e.NewHTTPClient(30 * time.Second)
resp, err := client.Do(req) resp, err := client.Do(req)
require.NoError(t, err, "FAIL: Request failed") require.NoError(t, err, "FAIL: Request failed")
defer resp.Body.Close() defer resp.Body.Close()
@ -165,12 +167,12 @@ func TestAuth_EmptyBearerToken(t *testing.T) {
defer cancel() defer cancel()
// Request with empty bearer token // Request with empty bearer token
req, err := http.NewRequestWithContext(ctx, http.MethodGet, GetGatewayURL()+"/v1/cache/health", nil) req, err := http.NewRequestWithContext(ctx, http.MethodGet, e2e.GetGatewayURL()+"/v1/cache/health", nil)
require.NoError(t, err, "FAIL: Could not create request") require.NoError(t, err, "FAIL: Could not create request")
req.Header.Set("Authorization", "Bearer ") req.Header.Set("Authorization", "Bearer ")
client := NewHTTPClient(30 * time.Second) client := e2e.NewHTTPClient(30 * time.Second)
resp, err := client.Do(req) resp, err := client.Do(req)
require.NoError(t, err, "FAIL: Request failed") require.NoError(t, err, "FAIL: Request failed")
defer resp.Body.Close() defer resp.Body.Close()
@ -182,7 +184,7 @@ func TestAuth_EmptyBearerToken(t *testing.T) {
} }
func TestAuth_DuplicateAuthHeaders(t *testing.T) { func TestAuth_DuplicateAuthHeaders(t *testing.T) {
if GetAPIKey() == "" { if e2e.GetAPIKey() == "" {
t.Skip("No API key configured") t.Skip("No API key configured")
} }
@ -190,12 +192,12 @@ func TestAuth_DuplicateAuthHeaders(t *testing.T) {
defer cancel() defer cancel()
// Request with both valid API key in Authorization header // Request with both valid API key in Authorization header
req := &HTTPRequest{ req := &e2e.HTTPRequest{
Method: http.MethodGet, Method: http.MethodGet,
URL: GetGatewayURL() + "/v1/cache/health", URL: e2e.GetGatewayURL() + "/v1/cache/health",
Headers: map[string]string{ Headers: map[string]string{
"Authorization": "Bearer " + GetAPIKey(), "Authorization": "Bearer " + e2e.GetAPIKey(),
"X-API-Key": GetAPIKey(), "X-API-Key": e2e.GetAPIKey(),
}, },
} }
@ -209,7 +211,7 @@ func TestAuth_DuplicateAuthHeaders(t *testing.T) {
} }
func TestAuth_CaseSensitiveAPIKey(t *testing.T) { func TestAuth_CaseSensitiveAPIKey(t *testing.T) {
apiKey := GetAPIKey() apiKey := e2e.GetAPIKey()
if apiKey == "" { if apiKey == "" {
t.Skip("No API key configured") t.Skip("No API key configured")
} }
@ -236,12 +238,12 @@ func TestAuth_CaseSensitiveAPIKey(t *testing.T) {
t.Skip("API key has no letters to change case") t.Skip("API key has no letters to change case")
} }
req, err := http.NewRequestWithContext(ctx, http.MethodGet, GetGatewayURL()+"/v1/cache/health", nil) req, err := http.NewRequestWithContext(ctx, http.MethodGet, e2e.GetGatewayURL()+"/v1/cache/health", nil)
require.NoError(t, err, "FAIL: Could not create request") require.NoError(t, err, "FAIL: Could not create request")
req.Header.Set("Authorization", "Bearer "+incorrectKey) req.Header.Set("Authorization", "Bearer "+incorrectKey)
client := NewHTTPClient(30 * time.Second) client := e2e.NewHTTPClient(30 * time.Second)
resp, err := client.Do(req) resp, err := client.Do(req)
require.NoError(t, err, "FAIL: Request failed") require.NoError(t, err, "FAIL: Request failed")
defer resp.Body.Close() defer resp.Body.Close()
@ -257,10 +259,10 @@ func TestAuth_HealthEndpointNoAuth(t *testing.T) {
defer cancel() defer cancel()
// Health endpoint at /v1/health should NOT require auth // Health endpoint at /v1/health should NOT require auth
req, err := http.NewRequestWithContext(ctx, http.MethodGet, GetGatewayURL()+"/v1/health", nil) req, err := http.NewRequestWithContext(ctx, http.MethodGet, e2e.GetGatewayURL()+"/v1/health", nil)
require.NoError(t, err, "FAIL: Could not create request") require.NoError(t, err, "FAIL: Could not create request")
client := NewHTTPClient(30 * time.Second) client := e2e.NewHTTPClient(30 * time.Second)
resp, err := client.Do(req) resp, err := client.Do(req)
require.NoError(t, err, "FAIL: Request failed") require.NoError(t, err, "FAIL: Request failed")
defer resp.Body.Close() defer resp.Body.Close()
@ -276,10 +278,10 @@ func TestAuth_StatusEndpointNoAuth(t *testing.T) {
defer cancel() defer cancel()
// Status endpoint at /v1/status should NOT require auth // Status endpoint at /v1/status should NOT require auth
req, err := http.NewRequestWithContext(ctx, http.MethodGet, GetGatewayURL()+"/v1/status", nil) req, err := http.NewRequestWithContext(ctx, http.MethodGet, e2e.GetGatewayURL()+"/v1/status", nil)
require.NoError(t, err, "FAIL: Could not create request") require.NoError(t, err, "FAIL: Could not create request")
client := NewHTTPClient(30 * time.Second) client := e2e.NewHTTPClient(30 * time.Second)
resp, err := client.Do(req) resp, err := client.Do(req)
require.NoError(t, err, "FAIL: Request failed") require.NoError(t, err, "FAIL: Request failed")
defer resp.Body.Close() defer resp.Body.Close()
@ -295,9 +297,9 @@ func TestAuth_DeploymentsWithoutAuth(t *testing.T) {
defer cancel() defer cancel()
// Request deployments endpoint without auth // Request deployments endpoint without auth
req := &HTTPRequest{ req := &e2e.HTTPRequest{
Method: http.MethodGet, Method: http.MethodGet,
URL: GetGatewayURL() + "/v1/deployments/list", URL: e2e.GetGatewayURL() + "/v1/deployments/list",
SkipAuth: true, SkipAuth: true,
} }
@ -315,9 +317,9 @@ func TestAuth_SQLiteWithoutAuth(t *testing.T) {
defer cancel() defer cancel()
// Request SQLite endpoint without auth // Request SQLite endpoint without auth
req := &HTTPRequest{ req := &e2e.HTTPRequest{
Method: http.MethodGet, Method: http.MethodGet,
URL: GetGatewayURL() + "/v1/db/sqlite/list", URL: e2e.GetGatewayURL() + "/v1/db/sqlite/list",
SkipAuth: true, SkipAuth: true,
} }

View File

@ -1,6 +1,6 @@
//go:build e2e //go:build e2e
package e2e package shared_test
import ( import (
"context" "context"
@ -8,17 +8,19 @@ import (
"net/http" "net/http"
"testing" "testing"
"time" "time"
e2e "github.com/DeBrosOfficial/network/e2e"
) )
func TestCache_Health(t *testing.T) { func TestCache_Health(t *testing.T) {
SkipIfMissingGateway(t) e2e.SkipIfMissingGateway(t)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel() defer cancel()
req := &HTTPRequest{ req := &e2e.HTTPRequest{
Method: http.MethodGet, Method: http.MethodGet,
URL: GetGatewayURL() + "/v1/cache/health", URL: e2e.GetGatewayURL() + "/v1/cache/health",
} }
body, status, err := req.Do(ctx) body, status, err := req.Do(ctx)
@ -31,7 +33,7 @@ func TestCache_Health(t *testing.T) {
} }
var resp map[string]interface{} var resp map[string]interface{}
if err := DecodeJSON(body, &resp); err != nil { if err := e2e.DecodeJSON(body, &resp); err != nil {
t.Fatalf("failed to decode response: %v", err) t.Fatalf("failed to decode response: %v", err)
} }
@ -45,19 +47,19 @@ func TestCache_Health(t *testing.T) {
} }
func TestCache_PutGet(t *testing.T) { func TestCache_PutGet(t *testing.T) {
SkipIfMissingGateway(t) e2e.SkipIfMissingGateway(t)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel() defer cancel()
dmap := GenerateDMapName() dmap := e2e.GenerateDMapName()
key := "test-key" key := "test-key"
value := "test-value" value := "test-value"
// Put value // Put value
putReq := &HTTPRequest{ putReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/cache/put", URL: e2e.GetGatewayURL() + "/v1/cache/put",
Body: map[string]interface{}{ Body: map[string]interface{}{
"dmap": dmap, "dmap": dmap,
"key": key, "key": key,
@ -75,9 +77,9 @@ func TestCache_PutGet(t *testing.T) {
} }
// Get value // Get value
getReq := &HTTPRequest{ getReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/cache/get", URL: e2e.GetGatewayURL() + "/v1/cache/get",
Body: map[string]interface{}{ Body: map[string]interface{}{
"dmap": dmap, "dmap": dmap,
"key": key, "key": key,
@ -94,7 +96,7 @@ func TestCache_PutGet(t *testing.T) {
} }
var getResp map[string]interface{} var getResp map[string]interface{}
if err := DecodeJSON(body, &getResp); err != nil { if err := e2e.DecodeJSON(body, &getResp); err != nil {
t.Fatalf("failed to decode response: %v", err) t.Fatalf("failed to decode response: %v", err)
} }
@ -104,12 +106,12 @@ func TestCache_PutGet(t *testing.T) {
} }
func TestCache_PutGetJSON(t *testing.T) { func TestCache_PutGetJSON(t *testing.T) {
SkipIfMissingGateway(t) e2e.SkipIfMissingGateway(t)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel() defer cancel()
dmap := GenerateDMapName() dmap := e2e.GenerateDMapName()
key := "json-key" key := "json-key"
jsonValue := map[string]interface{}{ jsonValue := map[string]interface{}{
"name": "John", "name": "John",
@ -118,9 +120,9 @@ func TestCache_PutGetJSON(t *testing.T) {
} }
// Put JSON value // Put JSON value
putReq := &HTTPRequest{ putReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/cache/put", URL: e2e.GetGatewayURL() + "/v1/cache/put",
Body: map[string]interface{}{ Body: map[string]interface{}{
"dmap": dmap, "dmap": dmap,
"key": key, "key": key,
@ -138,9 +140,9 @@ func TestCache_PutGetJSON(t *testing.T) {
} }
// Get JSON value // Get JSON value
getReq := &HTTPRequest{ getReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/cache/get", URL: e2e.GetGatewayURL() + "/v1/cache/get",
Body: map[string]interface{}{ Body: map[string]interface{}{
"dmap": dmap, "dmap": dmap,
"key": key, "key": key,
@ -157,7 +159,7 @@ func TestCache_PutGetJSON(t *testing.T) {
} }
var getResp map[string]interface{} var getResp map[string]interface{}
if err := DecodeJSON(body, &getResp); err != nil { if err := e2e.DecodeJSON(body, &getResp); err != nil {
t.Fatalf("failed to decode response: %v", err) t.Fatalf("failed to decode response: %v", err)
} }
@ -171,19 +173,19 @@ func TestCache_PutGetJSON(t *testing.T) {
} }
func TestCache_Delete(t *testing.T) { func TestCache_Delete(t *testing.T) {
SkipIfMissingGateway(t) e2e.SkipIfMissingGateway(t)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel() defer cancel()
dmap := GenerateDMapName() dmap := e2e.GenerateDMapName()
key := "delete-key" key := "delete-key"
value := "delete-value" value := "delete-value"
// Put value // Put value
putReq := &HTTPRequest{ putReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/cache/put", URL: e2e.GetGatewayURL() + "/v1/cache/put",
Body: map[string]interface{}{ Body: map[string]interface{}{
"dmap": dmap, "dmap": dmap,
"key": key, "key": key,
@ -197,9 +199,9 @@ func TestCache_Delete(t *testing.T) {
} }
// Delete value // Delete value
deleteReq := &HTTPRequest{ deleteReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/cache/delete", URL: e2e.GetGatewayURL() + "/v1/cache/delete",
Body: map[string]interface{}{ Body: map[string]interface{}{
"dmap": dmap, "dmap": dmap,
"key": key, "key": key,
@ -216,9 +218,9 @@ func TestCache_Delete(t *testing.T) {
} }
// Verify deletion // Verify deletion
getReq := &HTTPRequest{ getReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/cache/get", URL: e2e.GetGatewayURL() + "/v1/cache/get",
Body: map[string]interface{}{ Body: map[string]interface{}{
"dmap": dmap, "dmap": dmap,
"key": key, "key": key,
@ -233,19 +235,19 @@ func TestCache_Delete(t *testing.T) {
} }
func TestCache_TTL(t *testing.T) { func TestCache_TTL(t *testing.T) {
SkipIfMissingGateway(t) e2e.SkipIfMissingGateway(t)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel() defer cancel()
dmap := GenerateDMapName() dmap := e2e.GenerateDMapName()
key := "ttl-key" key := "ttl-key"
value := "ttl-value" value := "ttl-value"
// Put value with TTL // Put value with TTL
putReq := &HTTPRequest{ putReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/cache/put", URL: e2e.GetGatewayURL() + "/v1/cache/put",
Body: map[string]interface{}{ Body: map[string]interface{}{
"dmap": dmap, "dmap": dmap,
"key": key, "key": key,
@ -264,9 +266,9 @@ func TestCache_TTL(t *testing.T) {
} }
// Verify value exists // Verify value exists
getReq := &HTTPRequest{ getReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/cache/get", URL: e2e.GetGatewayURL() + "/v1/cache/get",
Body: map[string]interface{}{ Body: map[string]interface{}{
"dmap": dmap, "dmap": dmap,
"key": key, "key": key,
@ -279,7 +281,7 @@ func TestCache_TTL(t *testing.T) {
} }
// Wait for TTL expiry (2 seconds + buffer) // Wait for TTL expiry (2 seconds + buffer)
Delay(2500) e2e.Delay(2500)
// Verify value is expired // Verify value is expired
_, status, err = getReq.Do(ctx) _, status, err = getReq.Do(ctx)
@ -289,19 +291,19 @@ func TestCache_TTL(t *testing.T) {
} }
func TestCache_Scan(t *testing.T) { func TestCache_Scan(t *testing.T) {
SkipIfMissingGateway(t) e2e.SkipIfMissingGateway(t)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel() defer cancel()
dmap := GenerateDMapName() dmap := e2e.GenerateDMapName()
// Put multiple keys // Put multiple keys
keys := []string{"user-1", "user-2", "session-1", "session-2"} keys := []string{"user-1", "user-2", "session-1", "session-2"}
for _, key := range keys { for _, key := range keys {
putReq := &HTTPRequest{ putReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/cache/put", URL: e2e.GetGatewayURL() + "/v1/cache/put",
Body: map[string]interface{}{ Body: map[string]interface{}{
"dmap": dmap, "dmap": dmap,
"key": key, "key": key,
@ -316,9 +318,9 @@ func TestCache_Scan(t *testing.T) {
} }
// Scan all keys // Scan all keys
scanReq := &HTTPRequest{ scanReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/cache/scan", URL: e2e.GetGatewayURL() + "/v1/cache/scan",
Body: map[string]interface{}{ Body: map[string]interface{}{
"dmap": dmap, "dmap": dmap,
}, },
@ -334,7 +336,7 @@ func TestCache_Scan(t *testing.T) {
} }
var scanResp map[string]interface{} var scanResp map[string]interface{}
if err := DecodeJSON(body, &scanResp); err != nil { if err := e2e.DecodeJSON(body, &scanResp); err != nil {
t.Fatalf("failed to decode response: %v", err) t.Fatalf("failed to decode response: %v", err)
} }
@ -345,19 +347,19 @@ func TestCache_Scan(t *testing.T) {
} }
func TestCache_ScanWithRegex(t *testing.T) { func TestCache_ScanWithRegex(t *testing.T) {
SkipIfMissingGateway(t) e2e.SkipIfMissingGateway(t)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel() defer cancel()
dmap := GenerateDMapName() dmap := e2e.GenerateDMapName()
// Put keys with different patterns // Put keys with different patterns
keys := []string{"user-1", "user-2", "session-1", "session-2"} keys := []string{"user-1", "user-2", "session-1", "session-2"}
for _, key := range keys { for _, key := range keys {
putReq := &HTTPRequest{ putReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/cache/put", URL: e2e.GetGatewayURL() + "/v1/cache/put",
Body: map[string]interface{}{ Body: map[string]interface{}{
"dmap": dmap, "dmap": dmap,
"key": key, "key": key,
@ -372,9 +374,9 @@ func TestCache_ScanWithRegex(t *testing.T) {
} }
// Scan with regex pattern // Scan with regex pattern
scanReq := &HTTPRequest{ scanReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/cache/scan", URL: e2e.GetGatewayURL() + "/v1/cache/scan",
Body: map[string]interface{}{ Body: map[string]interface{}{
"dmap": dmap, "dmap": dmap,
"pattern": "^user-", "pattern": "^user-",
@ -391,7 +393,7 @@ func TestCache_ScanWithRegex(t *testing.T) {
} }
var scanResp map[string]interface{} var scanResp map[string]interface{}
if err := DecodeJSON(body, &scanResp); err != nil { if err := e2e.DecodeJSON(body, &scanResp); err != nil {
t.Fatalf("failed to decode response: %v", err) t.Fatalf("failed to decode response: %v", err)
} }
@ -402,19 +404,19 @@ func TestCache_ScanWithRegex(t *testing.T) {
} }
func TestCache_MultiGet(t *testing.T) { func TestCache_MultiGet(t *testing.T) {
SkipIfMissingGateway(t) e2e.SkipIfMissingGateway(t)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel() defer cancel()
dmap := GenerateDMapName() dmap := e2e.GenerateDMapName()
keys := []string{"key-1", "key-2", "key-3"} keys := []string{"key-1", "key-2", "key-3"}
// Put values // Put values
for i, key := range keys { for i, key := range keys {
putReq := &HTTPRequest{ putReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/cache/put", URL: e2e.GetGatewayURL() + "/v1/cache/put",
Body: map[string]interface{}{ Body: map[string]interface{}{
"dmap": dmap, "dmap": dmap,
"key": key, "key": key,
@ -429,9 +431,9 @@ func TestCache_MultiGet(t *testing.T) {
} }
// Multi-get // Multi-get
multiGetReq := &HTTPRequest{ multiGetReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/cache/mget", URL: e2e.GetGatewayURL() + "/v1/cache/mget",
Body: map[string]interface{}{ Body: map[string]interface{}{
"dmap": dmap, "dmap": dmap,
"keys": keys, "keys": keys,
@ -448,7 +450,7 @@ func TestCache_MultiGet(t *testing.T) {
} }
var mgetResp map[string]interface{} var mgetResp map[string]interface{}
if err := DecodeJSON(body, &mgetResp); err != nil { if err := e2e.DecodeJSON(body, &mgetResp); err != nil {
t.Fatalf("failed to decode response: %v", err) t.Fatalf("failed to decode response: %v", err)
} }
@ -459,14 +461,14 @@ func TestCache_MultiGet(t *testing.T) {
} }
func TestCache_MissingDMap(t *testing.T) { func TestCache_MissingDMap(t *testing.T) {
SkipIfMissingGateway(t) e2e.SkipIfMissingGateway(t)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel() defer cancel()
getReq := &HTTPRequest{ getReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/cache/get", URL: e2e.GetGatewayURL() + "/v1/cache/get",
Body: map[string]interface{}{ Body: map[string]interface{}{
"dmap": "", "dmap": "",
"key": "any-key", "key": "any-key",
@ -484,16 +486,16 @@ func TestCache_MissingDMap(t *testing.T) {
} }
func TestCache_MissingKey(t *testing.T) { func TestCache_MissingKey(t *testing.T) {
SkipIfMissingGateway(t) e2e.SkipIfMissingGateway(t)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel() defer cancel()
dmap := GenerateDMapName() dmap := e2e.GenerateDMapName()
getReq := &HTTPRequest{ getReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/cache/get", URL: e2e.GetGatewayURL() + "/v1/cache/get",
Body: map[string]interface{}{ Body: map[string]interface{}{
"dmap": dmap, "dmap": dmap,
"key": "non-existent-key", "key": "non-existent-key",

View File

@ -1,23 +1,25 @@
//go:build e2e //go:build e2e
package e2e package shared_test
import ( import (
"context" "context"
"net/http" "net/http"
"testing" "testing"
"time" "time"
e2e "github.com/DeBrosOfficial/network/e2e"
) )
func TestNetwork_Health(t *testing.T) { func TestNetwork_Health(t *testing.T) {
SkipIfMissingGateway(t) e2e.SkipIfMissingGateway(t)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel() defer cancel()
req := &HTTPRequest{ req := &e2e.HTTPRequest{
Method: http.MethodGet, Method: http.MethodGet,
URL: GetGatewayURL() + "/v1/health", URL: e2e.GetGatewayURL() + "/v1/health",
SkipAuth: true, SkipAuth: true,
} }
@ -31,7 +33,7 @@ func TestNetwork_Health(t *testing.T) {
} }
var resp map[string]interface{} var resp map[string]interface{}
if err := DecodeJSON(body, &resp); err != nil { if err := e2e.DecodeJSON(body, &resp); err != nil {
t.Fatalf("failed to decode response: %v", err) t.Fatalf("failed to decode response: %v", err)
} }
@ -41,14 +43,14 @@ func TestNetwork_Health(t *testing.T) {
} }
func TestNetwork_Status(t *testing.T) { func TestNetwork_Status(t *testing.T) {
SkipIfMissingGateway(t) e2e.SkipIfMissingGateway(t)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel() defer cancel()
req := &HTTPRequest{ req := &e2e.HTTPRequest{
Method: http.MethodGet, Method: http.MethodGet,
URL: GetGatewayURL() + "/v1/network/status", URL: e2e.GetGatewayURL() + "/v1/network/status",
} }
body, status, err := req.Do(ctx) body, status, err := req.Do(ctx)
@ -61,7 +63,7 @@ func TestNetwork_Status(t *testing.T) {
} }
var resp map[string]interface{} var resp map[string]interface{}
if err := DecodeJSON(body, &resp); err != nil { if err := e2e.DecodeJSON(body, &resp); err != nil {
t.Fatalf("failed to decode response: %v", err) t.Fatalf("failed to decode response: %v", err)
} }
@ -75,14 +77,14 @@ func TestNetwork_Status(t *testing.T) {
} }
func TestNetwork_Peers(t *testing.T) { func TestNetwork_Peers(t *testing.T) {
SkipIfMissingGateway(t) e2e.SkipIfMissingGateway(t)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel() defer cancel()
req := &HTTPRequest{ req := &e2e.HTTPRequest{
Method: http.MethodGet, Method: http.MethodGet,
URL: GetGatewayURL() + "/v1/network/peers", URL: e2e.GetGatewayURL() + "/v1/network/peers",
} }
body, status, err := req.Do(ctx) body, status, err := req.Do(ctx)
@ -95,7 +97,7 @@ func TestNetwork_Peers(t *testing.T) {
} }
var resp map[string]interface{} var resp map[string]interface{}
if err := DecodeJSON(body, &resp); err != nil { if err := e2e.DecodeJSON(body, &resp); err != nil {
t.Fatalf("failed to decode response: %v", err) t.Fatalf("failed to decode response: %v", err)
} }
@ -105,14 +107,14 @@ func TestNetwork_Peers(t *testing.T) {
} }
func TestNetwork_ProxyAnonSuccess(t *testing.T) { func TestNetwork_ProxyAnonSuccess(t *testing.T) {
SkipIfMissingGateway(t) e2e.SkipIfMissingGateway(t)
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel() defer cancel()
req := &HTTPRequest{ req := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/proxy/anon", URL: e2e.GetGatewayURL() + "/v1/proxy/anon",
Body: map[string]interface{}{ Body: map[string]interface{}{
"url": "https://httpbin.org/get", "url": "https://httpbin.org/get",
"method": "GET", "method": "GET",
@ -130,7 +132,7 @@ func TestNetwork_ProxyAnonSuccess(t *testing.T) {
} }
var resp map[string]interface{} var resp map[string]interface{}
if err := DecodeJSON(body, &resp); err != nil { if err := e2e.DecodeJSON(body, &resp); err != nil {
t.Fatalf("failed to decode response: %v", err) t.Fatalf("failed to decode response: %v", err)
} }
@ -144,14 +146,14 @@ func TestNetwork_ProxyAnonSuccess(t *testing.T) {
} }
func TestNetwork_ProxyAnonBadURL(t *testing.T) { func TestNetwork_ProxyAnonBadURL(t *testing.T) {
SkipIfMissingGateway(t) e2e.SkipIfMissingGateway(t)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel() defer cancel()
req := &HTTPRequest{ req := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/proxy/anon", URL: e2e.GetGatewayURL() + "/v1/proxy/anon",
Body: map[string]interface{}{ Body: map[string]interface{}{
"url": "http://localhost:1/nonexistent", "url": "http://localhost:1/nonexistent",
"method": "GET", "method": "GET",
@ -165,14 +167,14 @@ func TestNetwork_ProxyAnonBadURL(t *testing.T) {
} }
func TestNetwork_ProxyAnonPostRequest(t *testing.T) { func TestNetwork_ProxyAnonPostRequest(t *testing.T) {
SkipIfMissingGateway(t) e2e.SkipIfMissingGateway(t)
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel() defer cancel()
req := &HTTPRequest{ req := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/proxy/anon", URL: e2e.GetGatewayURL() + "/v1/proxy/anon",
Body: map[string]interface{}{ Body: map[string]interface{}{
"url": "https://httpbin.org/post", "url": "https://httpbin.org/post",
"method": "POST", "method": "POST",
@ -191,7 +193,7 @@ func TestNetwork_ProxyAnonPostRequest(t *testing.T) {
} }
var resp map[string]interface{} var resp map[string]interface{}
if err := DecodeJSON(body, &resp); err != nil { if err := e2e.DecodeJSON(body, &resp); err != nil {
t.Fatalf("failed to decode response: %v", err) t.Fatalf("failed to decode response: %v", err)
} }
@ -206,9 +208,9 @@ func TestNetwork_Unauthorized(t *testing.T) {
defer cancel() defer cancel()
// Create request without auth // Create request without auth
req := &HTTPRequest{ req := &e2e.HTTPRequest{
Method: http.MethodGet, Method: http.MethodGet,
URL: GetGatewayURL() + "/v1/network/status", URL: e2e.GetGatewayURL() + "/v1/network/status",
SkipAuth: true, SkipAuth: true,
} }

View File

@ -1,40 +1,42 @@
//go:build e2e //go:build e2e
package e2e package shared_test
import ( import (
"fmt" "fmt"
"sync" "sync"
"testing" "testing"
"time" "time"
e2e "github.com/DeBrosOfficial/network/e2e"
) )
// TestPubSub_SubscribePublish tests basic pub/sub functionality via WebSocket // TestPubSub_SubscribePublish tests basic pub/sub functionality via WebSocket
func TestPubSub_SubscribePublish(t *testing.T) { func TestPubSub_SubscribePublish(t *testing.T) {
SkipIfMissingGateway(t) e2e.SkipIfMissingGateway(t)
topic := GenerateTopic() topic := e2e.GenerateTopic()
message := "test-message-from-publisher" message := "test-message-from-publisher"
// Create subscriber first // Create subscriber first
subscriber, err := NewWSPubSubClient(t, topic) subscriber, err := e2e.NewWSPubSubClient(t, topic)
if err != nil { if err != nil {
t.Fatalf("failed to create subscriber: %v", err) t.Fatalf("failed to create subscriber: %v", err)
} }
defer subscriber.Close() defer subscriber.Close()
// Give subscriber time to register // Give subscriber time to register
Delay(200) e2e.Delay(200)
// Create publisher // Create publisher
publisher, err := NewWSPubSubClient(t, topic) publisher, err := e2e.NewWSPubSubClient(t, topic)
if err != nil { if err != nil {
t.Fatalf("failed to create publisher: %v", err) t.Fatalf("failed to create publisher: %v", err)
} }
defer publisher.Close() defer publisher.Close()
// Give connections time to stabilize // Give connections time to stabilize
Delay(200) e2e.Delay(200)
// Publish message // Publish message
if err := publisher.Publish([]byte(message)); err != nil { if err := publisher.Publish([]byte(message)); err != nil {
@ -54,37 +56,37 @@ func TestPubSub_SubscribePublish(t *testing.T) {
// TestPubSub_MultipleSubscribers tests that multiple subscribers receive the same message // TestPubSub_MultipleSubscribers tests that multiple subscribers receive the same message
func TestPubSub_MultipleSubscribers(t *testing.T) { func TestPubSub_MultipleSubscribers(t *testing.T) {
SkipIfMissingGateway(t) e2e.SkipIfMissingGateway(t)
topic := GenerateTopic() topic := e2e.GenerateTopic()
message1 := "message-1" message1 := "message-1"
message2 := "message-2" message2 := "message-2"
// Create two subscribers // Create two subscribers
sub1, err := NewWSPubSubClient(t, topic) sub1, err := e2e.NewWSPubSubClient(t, topic)
if err != nil { if err != nil {
t.Fatalf("failed to create subscriber1: %v", err) t.Fatalf("failed to create subscriber1: %v", err)
} }
defer sub1.Close() defer sub1.Close()
sub2, err := NewWSPubSubClient(t, topic) sub2, err := e2e.NewWSPubSubClient(t, topic)
if err != nil { if err != nil {
t.Fatalf("failed to create subscriber2: %v", err) t.Fatalf("failed to create subscriber2: %v", err)
} }
defer sub2.Close() defer sub2.Close()
// Give subscribers time to register // Give subscribers time to register
Delay(200) e2e.Delay(200)
// Create publisher // Create publisher
publisher, err := NewWSPubSubClient(t, topic) publisher, err := e2e.NewWSPubSubClient(t, topic)
if err != nil { if err != nil {
t.Fatalf("failed to create publisher: %v", err) t.Fatalf("failed to create publisher: %v", err)
} }
defer publisher.Close() defer publisher.Close()
// Give connections time to stabilize // Give connections time to stabilize
Delay(200) e2e.Delay(200)
// Publish first message // Publish first message
if err := publisher.Publish([]byte(message1)); err != nil { if err := publisher.Publish([]byte(message1)); err != nil {
@ -133,30 +135,30 @@ func TestPubSub_MultipleSubscribers(t *testing.T) {
// TestPubSub_Deduplication tests that multiple identical messages are all received // TestPubSub_Deduplication tests that multiple identical messages are all received
func TestPubSub_Deduplication(t *testing.T) { func TestPubSub_Deduplication(t *testing.T) {
SkipIfMissingGateway(t) e2e.SkipIfMissingGateway(t)
topic := GenerateTopic() topic := e2e.GenerateTopic()
message := "duplicate-test-message" message := "duplicate-test-message"
// Create subscriber // Create subscriber
subscriber, err := NewWSPubSubClient(t, topic) subscriber, err := e2e.NewWSPubSubClient(t, topic)
if err != nil { if err != nil {
t.Fatalf("failed to create subscriber: %v", err) t.Fatalf("failed to create subscriber: %v", err)
} }
defer subscriber.Close() defer subscriber.Close()
// Give subscriber time to register // Give subscriber time to register
Delay(200) e2e.Delay(200)
// Create publisher // Create publisher
publisher, err := NewWSPubSubClient(t, topic) publisher, err := e2e.NewWSPubSubClient(t, topic)
if err != nil { if err != nil {
t.Fatalf("failed to create publisher: %v", err) t.Fatalf("failed to create publisher: %v", err)
} }
defer publisher.Close() defer publisher.Close()
// Give connections time to stabilize // Give connections time to stabilize
Delay(200) e2e.Delay(200)
// Publish the same message multiple times // Publish the same message multiple times
for i := 0; i < 3; i++ { for i := 0; i < 3; i++ {
@ -164,7 +166,7 @@ func TestPubSub_Deduplication(t *testing.T) {
t.Fatalf("publish %d failed: %v", i, err) t.Fatalf("publish %d failed: %v", i, err)
} }
// Small delay between publishes // Small delay between publishes
Delay(50) e2e.Delay(50)
} }
// Receive messages - should get all (no dedup filter) // Receive messages - should get all (no dedup filter)
@ -185,30 +187,30 @@ func TestPubSub_Deduplication(t *testing.T) {
// TestPubSub_ConcurrentPublish tests concurrent message publishing // TestPubSub_ConcurrentPublish tests concurrent message publishing
func TestPubSub_ConcurrentPublish(t *testing.T) { func TestPubSub_ConcurrentPublish(t *testing.T) {
SkipIfMissingGateway(t) e2e.SkipIfMissingGateway(t)
topic := GenerateTopic() topic := e2e.GenerateTopic()
numMessages := 10 numMessages := 10
// Create subscriber // Create subscriber
subscriber, err := NewWSPubSubClient(t, topic) subscriber, err := e2e.NewWSPubSubClient(t, topic)
if err != nil { if err != nil {
t.Fatalf("failed to create subscriber: %v", err) t.Fatalf("failed to create subscriber: %v", err)
} }
defer subscriber.Close() defer subscriber.Close()
// Give subscriber time to register // Give subscriber time to register
Delay(200) e2e.Delay(200)
// Create publisher // Create publisher
publisher, err := NewWSPubSubClient(t, topic) publisher, err := e2e.NewWSPubSubClient(t, topic)
if err != nil { if err != nil {
t.Fatalf("failed to create publisher: %v", err) t.Fatalf("failed to create publisher: %v", err)
} }
defer publisher.Close() defer publisher.Close()
// Give connections time to stabilize // Give connections time to stabilize
Delay(200) e2e.Delay(200)
// Publish multiple messages concurrently // Publish multiple messages concurrently
var wg sync.WaitGroup var wg sync.WaitGroup
@ -241,45 +243,45 @@ func TestPubSub_ConcurrentPublish(t *testing.T) {
// TestPubSub_TopicIsolation tests that messages are isolated to their topics // TestPubSub_TopicIsolation tests that messages are isolated to their topics
func TestPubSub_TopicIsolation(t *testing.T) { func TestPubSub_TopicIsolation(t *testing.T) {
SkipIfMissingGateway(t) e2e.SkipIfMissingGateway(t)
topic1 := GenerateTopic() topic1 := e2e.GenerateTopic()
topic2 := GenerateTopic() topic2 := e2e.GenerateTopic()
msg1 := "message-on-topic1" msg1 := "message-on-topic1"
msg2 := "message-on-topic2" msg2 := "message-on-topic2"
// Create subscriber for topic1 // Create subscriber for topic1
sub1, err := NewWSPubSubClient(t, topic1) sub1, err := e2e.NewWSPubSubClient(t, topic1)
if err != nil { if err != nil {
t.Fatalf("failed to create subscriber1: %v", err) t.Fatalf("failed to create subscriber1: %v", err)
} }
defer sub1.Close() defer sub1.Close()
// Create subscriber for topic2 // Create subscriber for topic2
sub2, err := NewWSPubSubClient(t, topic2) sub2, err := e2e.NewWSPubSubClient(t, topic2)
if err != nil { if err != nil {
t.Fatalf("failed to create subscriber2: %v", err) t.Fatalf("failed to create subscriber2: %v", err)
} }
defer sub2.Close() defer sub2.Close()
// Give subscribers time to register // Give subscribers time to register
Delay(200) e2e.Delay(200)
// Create publishers // Create publishers
pub1, err := NewWSPubSubClient(t, topic1) pub1, err := e2e.NewWSPubSubClient(t, topic1)
if err != nil { if err != nil {
t.Fatalf("failed to create publisher1: %v", err) t.Fatalf("failed to create publisher1: %v", err)
} }
defer pub1.Close() defer pub1.Close()
pub2, err := NewWSPubSubClient(t, topic2) pub2, err := e2e.NewWSPubSubClient(t, topic2)
if err != nil { if err != nil {
t.Fatalf("failed to create publisher2: %v", err) t.Fatalf("failed to create publisher2: %v", err)
} }
defer pub2.Close() defer pub2.Close()
// Give connections time to stabilize // Give connections time to stabilize
Delay(200) e2e.Delay(200)
// Publish to topic2 first // Publish to topic2 first
if err := pub2.Publish([]byte(msg2)); err != nil { if err := pub2.Publish([]byte(msg2)); err != nil {
@ -312,29 +314,29 @@ func TestPubSub_TopicIsolation(t *testing.T) {
// TestPubSub_EmptyMessage tests sending and receiving empty messages // TestPubSub_EmptyMessage tests sending and receiving empty messages
func TestPubSub_EmptyMessage(t *testing.T) { func TestPubSub_EmptyMessage(t *testing.T) {
SkipIfMissingGateway(t) e2e.SkipIfMissingGateway(t)
topic := GenerateTopic() topic := e2e.GenerateTopic()
// Create subscriber // Create subscriber
subscriber, err := NewWSPubSubClient(t, topic) subscriber, err := e2e.NewWSPubSubClient(t, topic)
if err != nil { if err != nil {
t.Fatalf("failed to create subscriber: %v", err) t.Fatalf("failed to create subscriber: %v", err)
} }
defer subscriber.Close() defer subscriber.Close()
// Give subscriber time to register // Give subscriber time to register
Delay(200) e2e.Delay(200)
// Create publisher // Create publisher
publisher, err := NewWSPubSubClient(t, topic) publisher, err := e2e.NewWSPubSubClient(t, topic)
if err != nil { if err != nil {
t.Fatalf("failed to create publisher: %v", err) t.Fatalf("failed to create publisher: %v", err)
} }
defer publisher.Close() defer publisher.Close()
// Give connections time to stabilize // Give connections time to stabilize
Delay(200) e2e.Delay(200)
// Publish empty message // Publish empty message
if err := publisher.Publish([]byte("")); err != nil { if err := publisher.Publish([]byte("")); err != nil {
@ -354,9 +356,9 @@ func TestPubSub_EmptyMessage(t *testing.T) {
// TestPubSub_LargeMessage tests sending and receiving large messages // TestPubSub_LargeMessage tests sending and receiving large messages
func TestPubSub_LargeMessage(t *testing.T) { func TestPubSub_LargeMessage(t *testing.T) {
SkipIfMissingGateway(t) e2e.SkipIfMissingGateway(t)
topic := GenerateTopic() topic := e2e.GenerateTopic()
// Create a large message (100KB) // Create a large message (100KB)
largeMessage := make([]byte, 100*1024) largeMessage := make([]byte, 100*1024)
@ -365,24 +367,24 @@ func TestPubSub_LargeMessage(t *testing.T) {
} }
// Create subscriber // Create subscriber
subscriber, err := NewWSPubSubClient(t, topic) subscriber, err := e2e.NewWSPubSubClient(t, topic)
if err != nil { if err != nil {
t.Fatalf("failed to create subscriber: %v", err) t.Fatalf("failed to create subscriber: %v", err)
} }
defer subscriber.Close() defer subscriber.Close()
// Give subscriber time to register // Give subscriber time to register
Delay(200) e2e.Delay(200)
// Create publisher // Create publisher
publisher, err := NewWSPubSubClient(t, topic) publisher, err := e2e.NewWSPubSubClient(t, topic)
if err != nil { if err != nil {
t.Fatalf("failed to create publisher: %v", err) t.Fatalf("failed to create publisher: %v", err)
} }
defer publisher.Close() defer publisher.Close()
// Give connections time to stabilize // Give connections time to stabilize
Delay(200) e2e.Delay(200)
// Publish large message // Publish large message
if err := publisher.Publish(largeMessage); err != nil { if err := publisher.Publish(largeMessage); err != nil {
@ -409,30 +411,30 @@ func TestPubSub_LargeMessage(t *testing.T) {
// TestPubSub_RapidPublish tests rapid message publishing // TestPubSub_RapidPublish tests rapid message publishing
func TestPubSub_RapidPublish(t *testing.T) { func TestPubSub_RapidPublish(t *testing.T) {
SkipIfMissingGateway(t) e2e.SkipIfMissingGateway(t)
topic := GenerateTopic() topic := e2e.GenerateTopic()
numMessages := 50 numMessages := 50
// Create subscriber // Create subscriber
subscriber, err := NewWSPubSubClient(t, topic) subscriber, err := e2e.NewWSPubSubClient(t, topic)
if err != nil { if err != nil {
t.Fatalf("failed to create subscriber: %v", err) t.Fatalf("failed to create subscriber: %v", err)
} }
defer subscriber.Close() defer subscriber.Close()
// Give subscriber time to register // Give subscriber time to register
Delay(200) e2e.Delay(200)
// Create publisher // Create publisher
publisher, err := NewWSPubSubClient(t, topic) publisher, err := e2e.NewWSPubSubClient(t, topic)
if err != nil { if err != nil {
t.Fatalf("failed to create publisher: %v", err) t.Fatalf("failed to create publisher: %v", err)
} }
defer publisher.Close() defer publisher.Close()
// Give connections time to stabilize // Give connections time to stabilize
Delay(200) e2e.Delay(200)
// Publish messages rapidly // Publish messages rapidly
for i := 0; i < numMessages; i++ { for i := 0; i < numMessages; i++ {

View File

@ -1,6 +1,6 @@
//go:build e2e //go:build e2e
package e2e package shared_test
import ( import (
"context" "context"
@ -9,17 +9,19 @@ import (
"net/http" "net/http"
"testing" "testing"
"time" "time"
e2e "github.com/DeBrosOfficial/network/e2e"
) )
func TestPubSub_Presence(t *testing.T) { func TestPubSub_Presence(t *testing.T) {
SkipIfMissingGateway(t) e2e.SkipIfMissingGateway(t)
topic := GenerateTopic() topic := e2e.GenerateTopic()
memberID := "user123" memberID := "user123"
memberMeta := map[string]interface{}{"name": "Alice"} memberMeta := map[string]interface{}{"name": "Alice"}
// 1. Subscribe with presence // 1. Subscribe with presence
client1, err := NewWSPubSubPresenceClient(t, topic, memberID, memberMeta) client1, err := e2e.NewWSPubSubPresenceClient(t, topic, memberID, memberMeta)
if err != nil { if err != nil {
t.Fatalf("failed to create presence client: %v", err) t.Fatalf("failed to create presence client: %v", err)
} }
@ -48,9 +50,9 @@ func TestPubSub_Presence(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel() defer cancel()
req := &HTTPRequest{ req := &e2e.HTTPRequest{
Method: http.MethodGet, Method: http.MethodGet,
URL: fmt.Sprintf("%s/v1/pubsub/presence?topic=%s", GetGatewayURL(), topic), URL: fmt.Sprintf("%s/v1/pubsub/presence?topic=%s", e2e.GetGatewayURL(), topic),
} }
body, status, err := req.Do(ctx) body, status, err := req.Do(ctx)
@ -63,7 +65,7 @@ func TestPubSub_Presence(t *testing.T) {
} }
var resp map[string]interface{} var resp map[string]interface{}
if err := DecodeJSON(body, &resp); err != nil { if err := e2e.DecodeJSON(body, &resp); err != nil {
t.Fatalf("failed to decode response: %v", err) t.Fatalf("failed to decode response: %v", err)
} }
@ -83,7 +85,7 @@ func TestPubSub_Presence(t *testing.T) {
// 3. Subscribe second member // 3. Subscribe second member
memberID2 := "user456" memberID2 := "user456"
client2, err := NewWSPubSubPresenceClient(t, topic, memberID2, nil) client2, err := e2e.NewWSPubSubPresenceClient(t, topic, memberID2, nil)
if err != nil { if err != nil {
t.Fatalf("failed to create second presence client: %v", err) t.Fatalf("failed to create second presence client: %v", err)
} }
@ -119,4 +121,3 @@ func TestPubSub_Presence(t *testing.T) {
t.Fatalf("expected presence.leave for %s, got %v for %v", memberID2, event["type"], event["member_id"]) t.Fatalf("expected presence.leave for %s, got %v for %v", memberID2, event["type"], event["member_id"])
} }
} }

View File

@ -1,6 +1,6 @@
//go:build e2e //go:build e2e
package e2e package shared_test
import ( import (
"context" "context"
@ -8,21 +8,23 @@ import (
"net/http" "net/http"
"testing" "testing"
"time" "time"
e2e "github.com/DeBrosOfficial/network/e2e"
) )
func TestRQLite_CreateTable(t *testing.T) { func TestRQLite_CreateTable(t *testing.T) {
SkipIfMissingGateway(t) e2e.SkipIfMissingGateway(t)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel() defer cancel()
table := GenerateTableName() table := e2e.GenerateTableName()
// Cleanup table after test // Cleanup table after test
defer func() { defer func() {
dropReq := &HTTPRequest{ dropReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/rqlite/drop-table", URL: e2e.GetGatewayURL() + "/v1/rqlite/drop-table",
Body: map[string]interface{}{"table": table}, Body: map[string]interface{}{"table": table},
} }
dropReq.Do(context.Background()) dropReq.Do(context.Background())
@ -33,9 +35,9 @@ func TestRQLite_CreateTable(t *testing.T) {
table, table,
) )
req := &HTTPRequest{ req := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/rqlite/create-table", URL: e2e.GetGatewayURL() + "/v1/rqlite/create-table",
Body: map[string]interface{}{ Body: map[string]interface{}{
"schema": schema, "schema": schema,
}, },
@ -52,18 +54,18 @@ func TestRQLite_CreateTable(t *testing.T) {
} }
func TestRQLite_InsertQuery(t *testing.T) { func TestRQLite_InsertQuery(t *testing.T) {
SkipIfMissingGateway(t) e2e.SkipIfMissingGateway(t)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel() defer cancel()
table := GenerateTableName() table := e2e.GenerateTableName()
// Cleanup table after test // Cleanup table after test
defer func() { defer func() {
dropReq := &HTTPRequest{ dropReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/rqlite/drop-table", URL: e2e.GetGatewayURL() + "/v1/rqlite/drop-table",
Body: map[string]interface{}{"table": table}, Body: map[string]interface{}{"table": table},
} }
dropReq.Do(context.Background()) dropReq.Do(context.Background())
@ -75,9 +77,9 @@ func TestRQLite_InsertQuery(t *testing.T) {
) )
// Create table // Create table
createReq := &HTTPRequest{ createReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/rqlite/create-table", URL: e2e.GetGatewayURL() + "/v1/rqlite/create-table",
Body: map[string]interface{}{ Body: map[string]interface{}{
"schema": schema, "schema": schema,
}, },
@ -89,9 +91,9 @@ func TestRQLite_InsertQuery(t *testing.T) {
} }
// Insert rows // Insert rows
insertReq := &HTTPRequest{ insertReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/rqlite/transaction", URL: e2e.GetGatewayURL() + "/v1/rqlite/transaction",
Body: map[string]interface{}{ Body: map[string]interface{}{
"statements": []string{ "statements": []string{
fmt.Sprintf("INSERT INTO %s(name) VALUES ('alice')", table), fmt.Sprintf("INSERT INTO %s(name) VALUES ('alice')", table),
@ -106,9 +108,9 @@ func TestRQLite_InsertQuery(t *testing.T) {
} }
// Query rows // Query rows
queryReq := &HTTPRequest{ queryReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/rqlite/query", URL: e2e.GetGatewayURL() + "/v1/rqlite/query",
Body: map[string]interface{}{ Body: map[string]interface{}{
"sql": fmt.Sprintf("SELECT name FROM %s ORDER BY id", table), "sql": fmt.Sprintf("SELECT name FROM %s ORDER BY id", table),
}, },
@ -124,7 +126,7 @@ func TestRQLite_InsertQuery(t *testing.T) {
} }
var queryResp map[string]interface{} var queryResp map[string]interface{}
if err := DecodeJSON(body, &queryResp); err != nil { if err := e2e.DecodeJSON(body, &queryResp); err != nil {
t.Fatalf("failed to decode response: %v", err) t.Fatalf("failed to decode response: %v", err)
} }
@ -134,21 +136,21 @@ func TestRQLite_InsertQuery(t *testing.T) {
} }
func TestRQLite_DropTable(t *testing.T) { func TestRQLite_DropTable(t *testing.T) {
SkipIfMissingGateway(t) e2e.SkipIfMissingGateway(t)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel() defer cancel()
table := GenerateTableName() table := e2e.GenerateTableName()
schema := fmt.Sprintf( schema := fmt.Sprintf(
"CREATE TABLE IF NOT EXISTS %s (id INTEGER PRIMARY KEY, note TEXT)", "CREATE TABLE IF NOT EXISTS %s (id INTEGER PRIMARY KEY, note TEXT)",
table, table,
) )
// Create table // Create table
createReq := &HTTPRequest{ createReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/rqlite/create-table", URL: e2e.GetGatewayURL() + "/v1/rqlite/create-table",
Body: map[string]interface{}{ Body: map[string]interface{}{
"schema": schema, "schema": schema,
}, },
@ -160,9 +162,9 @@ func TestRQLite_DropTable(t *testing.T) {
} }
// Drop table // Drop table
dropReq := &HTTPRequest{ dropReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/rqlite/drop-table", URL: e2e.GetGatewayURL() + "/v1/rqlite/drop-table",
Body: map[string]interface{}{ Body: map[string]interface{}{
"table": table, "table": table,
}, },
@ -178,9 +180,9 @@ func TestRQLite_DropTable(t *testing.T) {
} }
// Verify table doesn't exist via schema // Verify table doesn't exist via schema
schemaReq := &HTTPRequest{ schemaReq := &e2e.HTTPRequest{
Method: http.MethodGet, Method: http.MethodGet,
URL: GetGatewayURL() + "/v1/rqlite/schema", URL: e2e.GetGatewayURL() + "/v1/rqlite/schema",
} }
body, status, err := schemaReq.Do(ctx) body, status, err := schemaReq.Do(ctx)
@ -190,7 +192,7 @@ func TestRQLite_DropTable(t *testing.T) {
} }
var schemaResp map[string]interface{} var schemaResp map[string]interface{}
if err := DecodeJSON(body, &schemaResp); err != nil { if err := e2e.DecodeJSON(body, &schemaResp); err != nil {
t.Logf("warning: failed to decode schema response: %v", err) t.Logf("warning: failed to decode schema response: %v", err)
return return
} }
@ -206,14 +208,14 @@ func TestRQLite_DropTable(t *testing.T) {
} }
func TestRQLite_Schema(t *testing.T) { func TestRQLite_Schema(t *testing.T) {
SkipIfMissingGateway(t) e2e.SkipIfMissingGateway(t)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel() defer cancel()
req := &HTTPRequest{ req := &e2e.HTTPRequest{
Method: http.MethodGet, Method: http.MethodGet,
URL: GetGatewayURL() + "/v1/rqlite/schema", URL: e2e.GetGatewayURL() + "/v1/rqlite/schema",
} }
body, status, err := req.Do(ctx) body, status, err := req.Do(ctx)
@ -226,7 +228,7 @@ func TestRQLite_Schema(t *testing.T) {
} }
var resp map[string]interface{} var resp map[string]interface{}
if err := DecodeJSON(body, &resp); err != nil { if err := e2e.DecodeJSON(body, &resp); err != nil {
t.Fatalf("failed to decode response: %v", err) t.Fatalf("failed to decode response: %v", err)
} }
@ -236,14 +238,14 @@ func TestRQLite_Schema(t *testing.T) {
} }
func TestRQLite_MalformedSQL(t *testing.T) { func TestRQLite_MalformedSQL(t *testing.T) {
SkipIfMissingGateway(t) e2e.SkipIfMissingGateway(t)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel() defer cancel()
req := &HTTPRequest{ req := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/rqlite/query", URL: e2e.GetGatewayURL() + "/v1/rqlite/query",
Body: map[string]interface{}{ Body: map[string]interface{}{
"sql": "SELECT * FROM nonexistent_table WHERE invalid syntax", "sql": "SELECT * FROM nonexistent_table WHERE invalid syntax",
}, },
@ -261,18 +263,18 @@ func TestRQLite_MalformedSQL(t *testing.T) {
} }
func TestRQLite_LargeTransaction(t *testing.T) { func TestRQLite_LargeTransaction(t *testing.T) {
SkipIfMissingGateway(t) e2e.SkipIfMissingGateway(t)
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel() defer cancel()
table := GenerateTableName() table := e2e.GenerateTableName()
// Cleanup table after test // Cleanup table after test
defer func() { defer func() {
dropReq := &HTTPRequest{ dropReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/rqlite/drop-table", URL: e2e.GetGatewayURL() + "/v1/rqlite/drop-table",
Body: map[string]interface{}{"table": table}, Body: map[string]interface{}{"table": table},
} }
dropReq.Do(context.Background()) dropReq.Do(context.Background())
@ -284,9 +286,9 @@ func TestRQLite_LargeTransaction(t *testing.T) {
) )
// Create table // Create table
createReq := &HTTPRequest{ createReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/rqlite/create-table", URL: e2e.GetGatewayURL() + "/v1/rqlite/create-table",
Body: map[string]interface{}{ Body: map[string]interface{}{
"schema": schema, "schema": schema,
}, },
@ -303,9 +305,9 @@ func TestRQLite_LargeTransaction(t *testing.T) {
statements = append(statements, fmt.Sprintf("INSERT INTO %s(value) VALUES (%d)", table, i)) statements = append(statements, fmt.Sprintf("INSERT INTO %s(value) VALUES (%d)", table, i))
} }
txReq := &HTTPRequest{ txReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/rqlite/transaction", URL: e2e.GetGatewayURL() + "/v1/rqlite/transaction",
Body: map[string]interface{}{ Body: map[string]interface{}{
"statements": statements, "statements": statements,
}, },
@ -317,9 +319,9 @@ func TestRQLite_LargeTransaction(t *testing.T) {
} }
// Verify all rows were inserted // Verify all rows were inserted
queryReq := &HTTPRequest{ queryReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/rqlite/query", URL: e2e.GetGatewayURL() + "/v1/rqlite/query",
Body: map[string]interface{}{ Body: map[string]interface{}{
"sql": fmt.Sprintf("SELECT COUNT(*) as count FROM %s", table), "sql": fmt.Sprintf("SELECT COUNT(*) as count FROM %s", table),
}, },
@ -331,7 +333,7 @@ func TestRQLite_LargeTransaction(t *testing.T) {
} }
var countResp map[string]interface{} var countResp map[string]interface{}
if err := DecodeJSON(body, &countResp); err != nil { if err := e2e.DecodeJSON(body, &countResp); err != nil {
t.Fatalf("failed to decode response: %v", err) t.Fatalf("failed to decode response: %v", err)
} }
@ -345,35 +347,35 @@ func TestRQLite_LargeTransaction(t *testing.T) {
} }
func TestRQLite_ForeignKeyMigration(t *testing.T) { func TestRQLite_ForeignKeyMigration(t *testing.T) {
SkipIfMissingGateway(t) e2e.SkipIfMissingGateway(t)
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel() defer cancel()
orgsTable := GenerateTableName() orgsTable := e2e.GenerateTableName()
usersTable := GenerateTableName() usersTable := e2e.GenerateTableName()
// Cleanup tables after test // Cleanup tables after test
defer func() { defer func() {
dropUsersReq := &HTTPRequest{ dropUsersReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/rqlite/drop-table", URL: e2e.GetGatewayURL() + "/v1/rqlite/drop-table",
Body: map[string]interface{}{"table": usersTable}, Body: map[string]interface{}{"table": usersTable},
} }
dropUsersReq.Do(context.Background()) dropUsersReq.Do(context.Background())
dropOrgsReq := &HTTPRequest{ dropOrgsReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/rqlite/drop-table", URL: e2e.GetGatewayURL() + "/v1/rqlite/drop-table",
Body: map[string]interface{}{"table": orgsTable}, Body: map[string]interface{}{"table": orgsTable},
} }
dropOrgsReq.Do(context.Background()) dropOrgsReq.Do(context.Background())
}() }()
// Create base tables // Create base tables
createOrgsReq := &HTTPRequest{ createOrgsReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/rqlite/create-table", URL: e2e.GetGatewayURL() + "/v1/rqlite/create-table",
Body: map[string]interface{}{ Body: map[string]interface{}{
"schema": fmt.Sprintf( "schema": fmt.Sprintf(
"CREATE TABLE IF NOT EXISTS %s (id INTEGER PRIMARY KEY, name TEXT)", "CREATE TABLE IF NOT EXISTS %s (id INTEGER PRIMARY KEY, name TEXT)",
@ -387,9 +389,9 @@ func TestRQLite_ForeignKeyMigration(t *testing.T) {
t.Fatalf("create orgs table failed: status %d, err %v", status, err) t.Fatalf("create orgs table failed: status %d, err %v", status, err)
} }
createUsersReq := &HTTPRequest{ createUsersReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/rqlite/create-table", URL: e2e.GetGatewayURL() + "/v1/rqlite/create-table",
Body: map[string]interface{}{ Body: map[string]interface{}{
"schema": fmt.Sprintf( "schema": fmt.Sprintf(
"CREATE TABLE IF NOT EXISTS %s (id INTEGER PRIMARY KEY, name TEXT, org_id INTEGER, age TEXT)", "CREATE TABLE IF NOT EXISTS %s (id INTEGER PRIMARY KEY, name TEXT, org_id INTEGER, age TEXT)",
@ -404,9 +406,9 @@ func TestRQLite_ForeignKeyMigration(t *testing.T) {
} }
// Seed data // Seed data
seedReq := &HTTPRequest{ seedReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/rqlite/transaction", URL: e2e.GetGatewayURL() + "/v1/rqlite/transaction",
Body: map[string]interface{}{ Body: map[string]interface{}{
"statements": []string{ "statements": []string{
fmt.Sprintf("INSERT INTO %s(id,name) VALUES (1,'org')", orgsTable), fmt.Sprintf("INSERT INTO %s(id,name) VALUES (1,'org')", orgsTable),
@ -421,9 +423,9 @@ func TestRQLite_ForeignKeyMigration(t *testing.T) {
} }
// Migrate: change age type and add FK // Migrate: change age type and add FK
migrationReq := &HTTPRequest{ migrationReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/rqlite/transaction", URL: e2e.GetGatewayURL() + "/v1/rqlite/transaction",
Body: map[string]interface{}{ Body: map[string]interface{}{
"statements": []string{ "statements": []string{
fmt.Sprintf( fmt.Sprintf(
@ -446,9 +448,9 @@ func TestRQLite_ForeignKeyMigration(t *testing.T) {
} }
// Verify data is intact // Verify data is intact
queryReq := &HTTPRequest{ queryReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/rqlite/query", URL: e2e.GetGatewayURL() + "/v1/rqlite/query",
Body: map[string]interface{}{ Body: map[string]interface{}{
"sql": fmt.Sprintf("SELECT name, org_id, age FROM %s", usersTable), "sql": fmt.Sprintf("SELECT name, org_id, age FROM %s", usersTable),
}, },
@ -460,7 +462,7 @@ func TestRQLite_ForeignKeyMigration(t *testing.T) {
} }
var queryResp map[string]interface{} var queryResp map[string]interface{}
if err := DecodeJSON(body, &queryResp); err != nil { if err := e2e.DecodeJSON(body, &queryResp); err != nil {
t.Fatalf("failed to decode response: %v", err) t.Fatalf("failed to decode response: %v", err)
} }
@ -470,14 +472,14 @@ func TestRQLite_ForeignKeyMigration(t *testing.T) {
} }
func TestRQLite_DropNonexistentTable(t *testing.T) { func TestRQLite_DropNonexistentTable(t *testing.T) {
SkipIfMissingGateway(t) e2e.SkipIfMissingGateway(t)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel() defer cancel()
dropReq := &HTTPRequest{ dropReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/rqlite/drop-table", URL: e2e.GetGatewayURL() + "/v1/rqlite/drop-table",
Body: map[string]interface{}{ Body: map[string]interface{}{
"table": "nonexistent_table_xyz_" + fmt.Sprintf("%d", time.Now().UnixNano()), "table": "nonexistent_table_xyz_" + fmt.Sprintf("%d", time.Now().UnixNano()),
}, },

View File

@ -1,6 +1,6 @@
//go:build e2e //go:build e2e
package e2e package shared_test
import ( import (
"bytes" "bytes"
@ -11,10 +11,12 @@ import (
"os" "os"
"testing" "testing"
"time" "time"
e2e "github.com/DeBrosOfficial/network/e2e"
) )
func TestServerless_DeployAndInvoke(t *testing.T) { func TestServerless_DeployAndInvoke(t *testing.T) {
SkipIfMissingGateway(t) e2e.SkipIfMissingGateway(t)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel() defer cancel()
@ -53,14 +55,14 @@ func TestServerless_DeployAndInvoke(t *testing.T) {
part.Write(wasmBytes) part.Write(wasmBytes)
writer.Close() writer.Close()
deployReq, _ := http.NewRequestWithContext(ctx, "POST", GetGatewayURL()+"/v1/functions", &buf) deployReq, _ := http.NewRequestWithContext(ctx, "POST", e2e.GetGatewayURL()+"/v1/functions", &buf)
deployReq.Header.Set("Content-Type", writer.FormDataContentType()) deployReq.Header.Set("Content-Type", writer.FormDataContentType())
if apiKey := GetAPIKey(); apiKey != "" { if apiKey := e2e.GetAPIKey(); apiKey != "" {
deployReq.Header.Set("Authorization", "Bearer "+apiKey) deployReq.Header.Set("Authorization", "Bearer "+apiKey)
} }
client := NewHTTPClient(1 * time.Minute) client := e2e.NewHTTPClient(1 * time.Minute)
resp, err := client.Do(deployReq) resp, err := client.Do(deployReq)
if err != nil { if err != nil {
t.Fatalf("deploy request failed: %v", err) t.Fatalf("deploy request failed: %v", err)
@ -74,10 +76,10 @@ func TestServerless_DeployAndInvoke(t *testing.T) {
// 2. Invoke function // 2. Invoke function
invokePayload := []byte(`{"name": "E2E Tester"}`) invokePayload := []byte(`{"name": "E2E Tester"}`)
invokeReq, _ := http.NewRequestWithContext(ctx, "POST", GetGatewayURL()+"/v1/functions/"+funcName+"/invoke?namespace="+namespace, bytes.NewReader(invokePayload)) invokeReq, _ := http.NewRequestWithContext(ctx, "POST", e2e.GetGatewayURL()+"/v1/functions/"+funcName+"/invoke?namespace="+namespace, bytes.NewReader(invokePayload))
invokeReq.Header.Set("Content-Type", "application/json") invokeReq.Header.Set("Content-Type", "application/json")
if apiKey := GetAPIKey(); apiKey != "" { if apiKey := e2e.GetAPIKey(); apiKey != "" {
invokeReq.Header.Set("Authorization", "Bearer "+apiKey) invokeReq.Header.Set("Authorization", "Bearer "+apiKey)
} }
@ -99,8 +101,8 @@ func TestServerless_DeployAndInvoke(t *testing.T) {
} }
// 3. List functions // 3. List functions
listReq, _ := http.NewRequestWithContext(ctx, "GET", GetGatewayURL()+"/v1/functions?namespace="+namespace, nil) listReq, _ := http.NewRequestWithContext(ctx, "GET", e2e.GetGatewayURL()+"/v1/functions?namespace="+namespace, nil)
if apiKey := GetAPIKey(); apiKey != "" { if apiKey := e2e.GetAPIKey(); apiKey != "" {
listReq.Header.Set("Authorization", "Bearer "+apiKey) listReq.Header.Set("Authorization", "Bearer "+apiKey)
} }
resp, err = client.Do(listReq) resp, err = client.Do(listReq)
@ -113,8 +115,8 @@ func TestServerless_DeployAndInvoke(t *testing.T) {
} }
// 4. Delete function // 4. Delete function
deleteReq, _ := http.NewRequestWithContext(ctx, "DELETE", GetGatewayURL()+"/v1/functions/"+funcName+"?namespace="+namespace, nil) deleteReq, _ := http.NewRequestWithContext(ctx, "DELETE", e2e.GetGatewayURL()+"/v1/functions/"+funcName+"?namespace="+namespace, nil)
if apiKey := GetAPIKey(); apiKey != "" { if apiKey := e2e.GetAPIKey(); apiKey != "" {
deleteReq.Header.Set("Authorization", "Bearer "+apiKey) deleteReq.Header.Set("Authorization", "Bearer "+apiKey)
} }
resp, err = client.Do(deleteReq) resp, err = client.Do(deleteReq)

View File

@ -1,6 +1,6 @@
//go:build e2e //go:build e2e
package e2e package shared_test
import ( import (
"bytes" "bytes"
@ -10,6 +10,8 @@ import (
"net/http" "net/http"
"testing" "testing"
"time" "time"
e2e "github.com/DeBrosOfficial/network/e2e"
) )
// uploadFile is a helper to upload a file to storage // uploadFile is a helper to upload a file to storage
@ -34,7 +36,7 @@ func uploadFile(t *testing.T, ctx context.Context, content []byte, filename stri
} }
// Create request // Create request
req, err := http.NewRequestWithContext(ctx, http.MethodPost, GetGatewayURL()+"/v1/storage/upload", &buf) req, err := http.NewRequestWithContext(ctx, http.MethodPost, e2e.GetGatewayURL()+"/v1/storage/upload", &buf)
if err != nil { if err != nil {
t.Fatalf("failed to create request: %v", err) t.Fatalf("failed to create request: %v", err)
} }
@ -42,13 +44,13 @@ func uploadFile(t *testing.T, ctx context.Context, content []byte, filename stri
req.Header.Set("Content-Type", writer.FormDataContentType()) req.Header.Set("Content-Type", writer.FormDataContentType())
// Add auth headers // Add auth headers
if jwt := GetJWT(); jwt != "" { if jwt := e2e.GetJWT(); jwt != "" {
req.Header.Set("Authorization", "Bearer "+jwt) req.Header.Set("Authorization", "Bearer "+jwt)
} else if apiKey := GetAPIKey(); apiKey != "" { } else if apiKey := e2e.GetAPIKey(); apiKey != "" {
req.Header.Set("Authorization", "Bearer "+apiKey) req.Header.Set("Authorization", "Bearer "+apiKey)
} }
client := NewHTTPClient(5 * time.Minute) client := e2e.NewHTTPClient(5 * time.Minute)
resp, err := client.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {
t.Fatalf("upload request failed: %v", err) t.Fatalf("upload request failed: %v", err)
@ -60,28 +62,20 @@ func uploadFile(t *testing.T, ctx context.Context, content []byte, filename stri
t.Fatalf("upload failed with status %d: %s", resp.StatusCode, string(body)) t.Fatalf("upload failed with status %d: %s", resp.StatusCode, string(body))
} }
result, err := DecodeJSONFromReader(resp.Body) body, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
t.Fatalf("failed to read upload response: %v", err)
}
var result map[string]interface{}
if err := e2e.DecodeJSON(body, &result); err != nil {
t.Fatalf("failed to decode upload response: %v", err) t.Fatalf("failed to decode upload response: %v", err)
} }
return result["cid"].(string) return result["cid"].(string)
} }
// DecodeJSON is a helper to decode JSON from io.ReadCloser
func DecodeJSONFromReader(rc io.ReadCloser) (map[string]interface{}, error) {
defer rc.Close()
body, err := io.ReadAll(rc)
if err != nil {
return nil, err
}
var result map[string]interface{}
err = DecodeJSON(body, &result)
return result, err
}
func TestStorage_UploadText(t *testing.T) { func TestStorage_UploadText(t *testing.T) {
SkipIfMissingGateway(t) e2e.SkipIfMissingGateway(t)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel() defer cancel()
@ -107,18 +101,18 @@ func TestStorage_UploadText(t *testing.T) {
} }
// Create request // Create request
req, err := http.NewRequestWithContext(ctx, http.MethodPost, GetGatewayURL()+"/v1/storage/upload", &buf) req, err := http.NewRequestWithContext(ctx, http.MethodPost, e2e.GetGatewayURL()+"/v1/storage/upload", &buf)
if err != nil { if err != nil {
t.Fatalf("failed to create request: %v", err) t.Fatalf("failed to create request: %v", err)
} }
req.Header.Set("Content-Type", writer.FormDataContentType()) req.Header.Set("Content-Type", writer.FormDataContentType())
if apiKey := GetAPIKey(); apiKey != "" { if apiKey := e2e.GetAPIKey(); apiKey != "" {
req.Header.Set("Authorization", "Bearer "+apiKey) req.Header.Set("Authorization", "Bearer "+apiKey)
} }
client := NewHTTPClient(5 * time.Minute) client := e2e.NewHTTPClient(5 * time.Minute)
resp, err := client.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {
t.Fatalf("upload request failed: %v", err) t.Fatalf("upload request failed: %v", err)
@ -132,7 +126,7 @@ func TestStorage_UploadText(t *testing.T) {
var result map[string]interface{} var result map[string]interface{}
body, _ := io.ReadAll(resp.Body) body, _ := io.ReadAll(resp.Body)
if err := DecodeJSON(body, &result); err != nil { if err := e2e.DecodeJSON(body, &result); err != nil {
t.Fatalf("failed to decode response: %v", err) t.Fatalf("failed to decode response: %v", err)
} }
@ -150,7 +144,7 @@ func TestStorage_UploadText(t *testing.T) {
} }
func TestStorage_UploadBinary(t *testing.T) { func TestStorage_UploadBinary(t *testing.T) {
SkipIfMissingGateway(t) e2e.SkipIfMissingGateway(t)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel() defer cancel()
@ -177,18 +171,18 @@ func TestStorage_UploadBinary(t *testing.T) {
} }
// Create request // Create request
req, err := http.NewRequestWithContext(ctx, http.MethodPost, GetGatewayURL()+"/v1/storage/upload", &buf) req, err := http.NewRequestWithContext(ctx, http.MethodPost, e2e.GetGatewayURL()+"/v1/storage/upload", &buf)
if err != nil { if err != nil {
t.Fatalf("failed to create request: %v", err) t.Fatalf("failed to create request: %v", err)
} }
req.Header.Set("Content-Type", writer.FormDataContentType()) req.Header.Set("Content-Type", writer.FormDataContentType())
if apiKey := GetAPIKey(); apiKey != "" { if apiKey := e2e.GetAPIKey(); apiKey != "" {
req.Header.Set("Authorization", "Bearer "+apiKey) req.Header.Set("Authorization", "Bearer "+apiKey)
} }
client := NewHTTPClient(5 * time.Minute) client := e2e.NewHTTPClient(5 * time.Minute)
resp, err := client.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {
t.Fatalf("upload request failed: %v", err) t.Fatalf("upload request failed: %v", err)
@ -202,7 +196,7 @@ func TestStorage_UploadBinary(t *testing.T) {
var result map[string]interface{} var result map[string]interface{}
body, _ := io.ReadAll(resp.Body) body, _ := io.ReadAll(resp.Body)
if err := DecodeJSON(body, &result); err != nil { if err := e2e.DecodeJSON(body, &result); err != nil {
t.Fatalf("failed to decode response: %v", err) t.Fatalf("failed to decode response: %v", err)
} }
@ -212,7 +206,7 @@ func TestStorage_UploadBinary(t *testing.T) {
} }
func TestStorage_UploadLarge(t *testing.T) { func TestStorage_UploadLarge(t *testing.T) {
SkipIfMissingGateway(t) e2e.SkipIfMissingGateway(t)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel() defer cancel()
@ -239,18 +233,18 @@ func TestStorage_UploadLarge(t *testing.T) {
} }
// Create request // Create request
req, err := http.NewRequestWithContext(ctx, http.MethodPost, GetGatewayURL()+"/v1/storage/upload", &buf) req, err := http.NewRequestWithContext(ctx, http.MethodPost, e2e.GetGatewayURL()+"/v1/storage/upload", &buf)
if err != nil { if err != nil {
t.Fatalf("failed to create request: %v", err) t.Fatalf("failed to create request: %v", err)
} }
req.Header.Set("Content-Type", writer.FormDataContentType()) req.Header.Set("Content-Type", writer.FormDataContentType())
if apiKey := GetAPIKey(); apiKey != "" { if apiKey := e2e.GetAPIKey(); apiKey != "" {
req.Header.Set("Authorization", "Bearer "+apiKey) req.Header.Set("Authorization", "Bearer "+apiKey)
} }
client := NewHTTPClient(5 * time.Minute) client := e2e.NewHTTPClient(5 * time.Minute)
resp, err := client.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {
t.Fatalf("upload request failed: %v", err) t.Fatalf("upload request failed: %v", err)
@ -264,7 +258,7 @@ func TestStorage_UploadLarge(t *testing.T) {
var result map[string]interface{} var result map[string]interface{}
body, _ := io.ReadAll(resp.Body) body, _ := io.ReadAll(resp.Body)
if err := DecodeJSON(body, &result); err != nil { if err := e2e.DecodeJSON(body, &result); err != nil {
t.Fatalf("failed to decode response: %v", err) t.Fatalf("failed to decode response: %v", err)
} }
@ -274,7 +268,7 @@ func TestStorage_UploadLarge(t *testing.T) {
} }
func TestStorage_PinUnpin(t *testing.T) { func TestStorage_PinUnpin(t *testing.T) {
SkipIfMissingGateway(t) e2e.SkipIfMissingGateway(t)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel() defer cancel()
@ -299,18 +293,18 @@ func TestStorage_PinUnpin(t *testing.T) {
} }
// Create upload request // Create upload request
req, err := http.NewRequestWithContext(ctx, http.MethodPost, GetGatewayURL()+"/v1/storage/upload", &buf) req, err := http.NewRequestWithContext(ctx, http.MethodPost, e2e.GetGatewayURL()+"/v1/storage/upload", &buf)
if err != nil { if err != nil {
t.Fatalf("failed to create request: %v", err) t.Fatalf("failed to create request: %v", err)
} }
req.Header.Set("Content-Type", writer.FormDataContentType()) req.Header.Set("Content-Type", writer.FormDataContentType())
if apiKey := GetAPIKey(); apiKey != "" { if apiKey := e2e.GetAPIKey(); apiKey != "" {
req.Header.Set("Authorization", "Bearer "+apiKey) req.Header.Set("Authorization", "Bearer "+apiKey)
} }
client := NewHTTPClient(5 * time.Minute) client := e2e.NewHTTPClient(5 * time.Minute)
resp, err := client.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {
t.Fatalf("upload failed: %v", err) t.Fatalf("upload failed: %v", err)
@ -319,7 +313,7 @@ func TestStorage_PinUnpin(t *testing.T) {
var uploadResult map[string]interface{} var uploadResult map[string]interface{}
body, _ := io.ReadAll(resp.Body) body, _ := io.ReadAll(resp.Body)
if err := DecodeJSON(body, &uploadResult); err != nil { if err := e2e.DecodeJSON(body, &uploadResult); err != nil {
t.Fatalf("failed to decode upload response: %v", err) t.Fatalf("failed to decode upload response: %v", err)
} }
@ -333,9 +327,9 @@ func TestStorage_PinUnpin(t *testing.T) {
} }
// Pin the file // Pin the file
pinReq := &HTTPRequest{ pinReq := &e2e.HTTPRequest{
Method: http.MethodPost, Method: http.MethodPost,
URL: GetGatewayURL() + "/v1/storage/pin", URL: e2e.GetGatewayURL() + "/v1/storage/pin",
Body: map[string]interface{}{ Body: map[string]interface{}{
"cid": cid, "cid": cid,
"name": "pinned-file", "name": "pinned-file",
@ -352,7 +346,7 @@ func TestStorage_PinUnpin(t *testing.T) {
} }
var pinResult map[string]interface{} var pinResult map[string]interface{}
if err := DecodeJSON(body2, &pinResult); err != nil { if err := e2e.DecodeJSON(body2, &pinResult); err != nil {
t.Fatalf("failed to decode pin response: %v", err) t.Fatalf("failed to decode pin response: %v", err)
} }
@ -361,9 +355,9 @@ func TestStorage_PinUnpin(t *testing.T) {
} }
// Unpin the file // Unpin the file
unpinReq := &HTTPRequest{ unpinReq := &e2e.HTTPRequest{
Method: http.MethodDelete, Method: http.MethodDelete,
URL: GetGatewayURL() + "/v1/storage/unpin/" + cid, URL: e2e.GetGatewayURL() + "/v1/storage/unpin/" + cid,
} }
body3, status, err := unpinReq.Do(ctx) body3, status, err := unpinReq.Do(ctx)
@ -377,7 +371,7 @@ func TestStorage_PinUnpin(t *testing.T) {
} }
func TestStorage_Status(t *testing.T) { func TestStorage_Status(t *testing.T) {
SkipIfMissingGateway(t) e2e.SkipIfMissingGateway(t)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel() defer cancel()
@ -402,18 +396,18 @@ func TestStorage_Status(t *testing.T) {
} }
// Create upload request // Create upload request
req, err := http.NewRequestWithContext(ctx, http.MethodPost, GetGatewayURL()+"/v1/storage/upload", &buf) req, err := http.NewRequestWithContext(ctx, http.MethodPost, e2e.GetGatewayURL()+"/v1/storage/upload", &buf)
if err != nil { if err != nil {
t.Fatalf("failed to create request: %v", err) t.Fatalf("failed to create request: %v", err)
} }
req.Header.Set("Content-Type", writer.FormDataContentType()) req.Header.Set("Content-Type", writer.FormDataContentType())
if apiKey := GetAPIKey(); apiKey != "" { if apiKey := e2e.GetAPIKey(); apiKey != "" {
req.Header.Set("Authorization", "Bearer "+apiKey) req.Header.Set("Authorization", "Bearer "+apiKey)
} }
client := NewHTTPClient(5 * time.Minute) client := e2e.NewHTTPClient(5 * time.Minute)
resp, err := client.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {
t.Fatalf("upload failed: %v", err) t.Fatalf("upload failed: %v", err)
@ -422,16 +416,16 @@ func TestStorage_Status(t *testing.T) {
var uploadResult map[string]interface{} var uploadResult map[string]interface{}
body, _ := io.ReadAll(resp.Body) body, _ := io.ReadAll(resp.Body)
if err := DecodeJSON(body, &uploadResult); err != nil { if err := e2e.DecodeJSON(body, &uploadResult); err != nil {
t.Fatalf("failed to decode upload response: %v", err) t.Fatalf("failed to decode upload response: %v", err)
} }
cid := uploadResult["cid"].(string) cid := uploadResult["cid"].(string)
// Get status // Get status
statusReq := &HTTPRequest{ statusReq := &e2e.HTTPRequest{
Method: http.MethodGet, Method: http.MethodGet,
URL: GetGatewayURL() + "/v1/storage/status/" + cid, URL: e2e.GetGatewayURL() + "/v1/storage/status/" + cid,
} }
statusBody, status, err := statusReq.Do(ctx) statusBody, status, err := statusReq.Do(ctx)
@ -444,7 +438,7 @@ func TestStorage_Status(t *testing.T) {
} }
var statusResult map[string]interface{} var statusResult map[string]interface{}
if err := DecodeJSON(statusBody, &statusResult); err != nil { if err := e2e.DecodeJSON(statusBody, &statusResult); err != nil {
t.Fatalf("failed to decode status response: %v", err) t.Fatalf("failed to decode status response: %v", err)
} }
@ -454,14 +448,14 @@ func TestStorage_Status(t *testing.T) {
} }
func TestStorage_InvalidCID(t *testing.T) { func TestStorage_InvalidCID(t *testing.T) {
SkipIfMissingGateway(t) e2e.SkipIfMissingGateway(t)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel() defer cancel()
statusReq := &HTTPRequest{ statusReq := &e2e.HTTPRequest{
Method: http.MethodGet, Method: http.MethodGet,
URL: GetGatewayURL() + "/v1/storage/status/QmInvalidCID123456789", URL: e2e.GetGatewayURL() + "/v1/storage/status/QmInvalidCID123456789",
} }
_, status, err := statusReq.Do(ctx) _, status, err := statusReq.Do(ctx)
@ -475,7 +469,7 @@ func TestStorage_InvalidCID(t *testing.T) {
} }
func TestStorage_GetByteRange(t *testing.T) { func TestStorage_GetByteRange(t *testing.T) {
SkipIfMissingGateway(t) e2e.SkipIfMissingGateway(t)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel() defer cancel()
@ -500,18 +494,18 @@ func TestStorage_GetByteRange(t *testing.T) {
} }
// Create upload request // Create upload request
req, err := http.NewRequestWithContext(ctx, http.MethodPost, GetGatewayURL()+"/v1/storage/upload", &buf) req, err := http.NewRequestWithContext(ctx, http.MethodPost, e2e.GetGatewayURL()+"/v1/storage/upload", &buf)
if err != nil { if err != nil {
t.Fatalf("failed to create request: %v", err) t.Fatalf("failed to create request: %v", err)
} }
req.Header.Set("Content-Type", writer.FormDataContentType()) req.Header.Set("Content-Type", writer.FormDataContentType())
if apiKey := GetAPIKey(); apiKey != "" { if apiKey := e2e.GetAPIKey(); apiKey != "" {
req.Header.Set("Authorization", "Bearer "+apiKey) req.Header.Set("Authorization", "Bearer "+apiKey)
} }
client := NewHTTPClient(5 * time.Minute) client := e2e.NewHTTPClient(5 * time.Minute)
resp, err := client.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {
t.Fatalf("upload failed: %v", err) t.Fatalf("upload failed: %v", err)
@ -520,19 +514,19 @@ func TestStorage_GetByteRange(t *testing.T) {
var uploadResult map[string]interface{} var uploadResult map[string]interface{}
body, _ := io.ReadAll(resp.Body) body, _ := io.ReadAll(resp.Body)
if err := DecodeJSON(body, &uploadResult); err != nil { if err := e2e.DecodeJSON(body, &uploadResult); err != nil {
t.Fatalf("failed to decode upload response: %v", err) t.Fatalf("failed to decode upload response: %v", err)
} }
cid := uploadResult["cid"].(string) cid := uploadResult["cid"].(string)
// Get full content // Get full content
getReq, err := http.NewRequestWithContext(ctx, http.MethodGet, GetGatewayURL()+"/v1/storage/get/"+cid, nil) getReq, err := http.NewRequestWithContext(ctx, http.MethodGet, e2e.GetGatewayURL()+"/v1/storage/get/"+cid, nil)
if err != nil { if err != nil {
t.Fatalf("failed to create get request: %v", err) t.Fatalf("failed to create get request: %v", err)
} }
if apiKey := GetAPIKey(); apiKey != "" { if apiKey := e2e.GetAPIKey(); apiKey != "" {
getReq.Header.Set("Authorization", "Bearer "+apiKey) getReq.Header.Set("Authorization", "Bearer "+apiKey)
} }