139 lines
4.7 KiB
Go
139 lines
4.7 KiB
Go
package api
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"os"
|
|
"regexp"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
hub "git.ipao.vip/rogee/creator-hub/internal/environment"
|
|
)
|
|
|
|
func TestAnonymousBrowserUsesIndependentProfile(t *testing.T) {
|
|
databaseURL := os.Getenv("CREATORHUB_POSTGRES_TEST_URL")
|
|
if databaseURL == "" {
|
|
t.Skip("set CREATORHUB_POSTGRES_TEST_URL to run PostgreSQL integration coverage")
|
|
}
|
|
for _, withTemplate := range []bool{false, true} {
|
|
name := "without_template"
|
|
if withTemplate {
|
|
name = "with_template"
|
|
}
|
|
t.Run(name, func(t *testing.T) {
|
|
ctx := context.Background()
|
|
store, err := hub.Open(ctx, isolatedControlPlaneDatabaseURL(t, databaseURL))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Cleanup(func() { _ = store.Close() })
|
|
|
|
var mu sync.Mutex
|
|
var payloads []struct {
|
|
Alias string `json:"alias"`
|
|
ProfileID string `json:"profile_id"`
|
|
}
|
|
var purgedProfiles []string
|
|
profilePattern := regexp.MustCompile(`^[A-Za-z0-9][A-Za-z0-9._:@/-]{0,127}$`)
|
|
fakeChannelStore := newMemoryChannelStore(hub.Gateway{ID: 90, Name: "gw-profile-test", AccessKey: "anon-gateway-access-key-00090"})
|
|
SetGatewayChannelStore(fakeChannelStore)
|
|
server := (&gatewayChannelTestServer{store: fakeChannelStore, URL: startChannelServer(t, fakeChannelStore)})
|
|
deadline := time.Now().Add(2 * time.Second)
|
|
for time.Now().Before(deadline) && sessionByGateway(90) == nil {
|
|
time.Sleep(5 * time.Millisecond)
|
|
}
|
|
connectFakeGateway(t, server.URL, "anon-gateway-access-key-00090", func(task taskFrame) *TaskResult {
|
|
mu.Lock()
|
|
defer mu.Unlock()
|
|
switch {
|
|
case task.Method == "POST" && task.Path == "/v1/browsers":
|
|
var payload struct {
|
|
Alias string `json:"alias"`
|
|
ProfileID string `json:"profile_id"`
|
|
}
|
|
if err := json.Unmarshal(task.Payload, &payload); err != nil {
|
|
return &TaskResult{Status: 400, Body: []byte(`{"error":"bad payload"}`)}
|
|
}
|
|
payloads = append(payloads, payload)
|
|
if !profilePattern.MatchString(payload.ProfileID) {
|
|
return &TaskResult{Status: 400, Body: []byte(`{"detail":"profile_id is invalid"}`)}
|
|
}
|
|
body, _ := json.Marshal(map[string]any{
|
|
"id": strings.Repeat("a", 64), "network_id": "native-" + strings.Repeat("b", 32),
|
|
"alias": payload.Alias, "binding_version": 1, "state": "running",
|
|
})
|
|
return &TaskResult{Status: 201, Body: body}
|
|
case task.Method == "GET" && task.Path == "/v1/browsers":
|
|
return &TaskResult{Status: 200, Body: []byte(`[]`)}
|
|
case task.Method == "DELETE" && strings.HasPrefix(task.Path, "/v1/browsers/"):
|
|
var payload struct {
|
|
PurgeProfile bool `json:"purge_profile"`
|
|
}
|
|
_ = json.Unmarshal(task.Payload, &payload)
|
|
if !payload.PurgeProfile {
|
|
t.Error("anonymous cleanup must purge its temporary profile")
|
|
}
|
|
// The gateway purges the profile saved in the runtime selected by alias.
|
|
alias := strings.TrimPrefix(task.Path, "/v1/browsers/")
|
|
for _, created := range payloads {
|
|
if created.Alias == alias {
|
|
purgedProfiles = append(purgedProfiles, created.ProfileID)
|
|
break
|
|
}
|
|
}
|
|
return &TaskResult{Status: 204}
|
|
default:
|
|
return &TaskResult{Status: 404, Error: "unexpected " + task.Method + " " + task.Path}
|
|
}
|
|
})
|
|
gateway, err := store.CreateGateway(ctx, "gw-profile-test")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
_ = gateway
|
|
var template hub.EnvironmentContext
|
|
if withTemplate {
|
|
template, err = store.CreateStandaloneEnv(ctx, "gw-profile-test", hub.Fingerprint{}, 4096)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
for i := 0; i < 2; i++ {
|
|
lease, err := newAnonymousBrowser(ctx, store)
|
|
if err != nil {
|
|
t.Fatalf("anonymous browser creation failed: %v", err)
|
|
}
|
|
if err := lease.close(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
payload := payloads[i]
|
|
if payload.ProfileID != payload.Alias || !strings.HasPrefix(payload.ProfileID, "anon-") {
|
|
t.Fatalf("anonymous profile must use its own alias: %+v", payload)
|
|
}
|
|
if purgedProfiles[i] != payload.ProfileID {
|
|
t.Fatalf("cleanup purged another profile: got %q want %q", purgedProfiles[i], payload.ProfileID)
|
|
}
|
|
if withTemplate && payload.ProfileID == template.ProfileID {
|
|
t.Fatal("anonymous browser reused an existing environment profile")
|
|
}
|
|
}
|
|
if payloads[0].ProfileID == payloads[1].ProfileID {
|
|
t.Fatal("anonymous browsers must not share a profile")
|
|
}
|
|
if withTemplate {
|
|
stored, err := store.GetEnvironmentContext(ctx, template.Alias)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if stored.ProfileID != template.ProfileID || stored.Fingerprint.Seed != template.Fingerprint.Seed {
|
|
t.Fatal("anonymous browser changed the existing environment identity")
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|