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") } } }) } }