package environment import ( "context" "errors" "testing" ) func TestEnvironmentMemoryLimitPersistence(t *testing.T) { ctx := context.Background() s := seedFingerprintAccount(t, ctx, "memory-owner") for _, limit := range []int{0, 512, 4096, 65536} { env, err := s.CreateStandaloneEnv(ctx, "gw-fp", Fingerprint{}, limit) if err != nil { t.Fatal(err) } expected := limit if expected == 0 { expected = 2048 } got, err := s.GetEnv(ctx, env.Alias) if err != nil || got.MemoryLimitMB != expected { t.Fatalf("read limit: %+v %v", got, err) } pending, err := s.ListPendingEnvironments(ctx) if err != nil || pending[0].MemoryLimitMB != expected { t.Fatalf("pending: %+v %v", pending, err) } } for _, limit := range []int{-1, 511, 65537} { if _, err := s.CreateStandaloneEnv(ctx, "gw-fp", Fingerprint{}, limit); !errors.Is(err, ErrInvalid) { t.Fatalf("invalid %d: %v", limit, err) } if _, _, err := s.CreateBoundEnv(ctx, Env{Alias: "memory-owner", Name: "memory", Gateway: "gw-fp", MemoryLimitMB: limit}, "memory-owner", ""); !errors.Is(err, ErrInvalid) { t.Fatalf("bound invalid %d: %v", limit, err) } } before, _, err := s.CreateBoundEnv(ctx, Env{Alias: "memory-owner", Name: "memory", Gateway: "gw-fp", MemoryLimitMB: 4096, Fingerprint: Fingerprint{Timezone: "Asia/Shanghai"}}, "memory-owner", "") if err != nil { t.Fatal(err) } if _, err := s.db.ExecContext(ctx, `UPDATE browser_env SET runtime_id='running-memory',runtime_lease_until=now()+interval '1 hour',fingerprint=jsonb_set(fingerprint,'{hardware_concurrency}','0') WHERE alias=$1`, before.Alias); err != nil { t.Fatal(err) } before, err = s.GetEnvironmentContext(ctx, before.Alias) if err != nil { t.Fatal(err) } var fingerprintJSON string if err := s.db.QueryRowContext(ctx, `SELECT fingerprint::text FROM browser_env WHERE alias=$1`, before.Alias).Scan(&fingerprintJSON); err != nil { t.Fatal(err) } limit := 8192 after, err := s.UpdateEnvironmentSettings(ctx, before.Alias, nil, &limit) if err != nil { t.Fatal(err) } if after.MemoryLimitMB != limit || after.Fingerprint != before.Fingerprint || after.ProfileID != before.ProfileID || after.RuntimeID != before.RuntimeID || after.BindingVersion != before.BindingVersion || after.Gateway != before.Gateway { t.Fatalf("memory update changed identity: before=%+v after=%+v", before, after) } var updatedJSON string if err := s.db.QueryRowContext(ctx, `SELECT fingerprint::text FROM browser_env WHERE alias=$1`, before.Alias).Scan(&updatedJSON); err != nil { t.Fatal(err) } if updatedJSON != fingerprintJSON { t.Fatalf("memory-only edit rewrote fingerprint: before=%s after=%s", fingerprintJSON, updatedJSON) } for _, limit := range []int{0, 511, 65537} { if _, err := s.UpdateEnvironmentSettings(ctx, before.Alias, &Fingerprint{Timezone: "Asia/Tokyo"}, &limit); !errors.Is(err, ErrInvalid) { t.Fatalf("invalid update: %v", err) } } got, err := s.GetEnvironmentContext(ctx, before.Alias) if err != nil || got.Fingerprint != before.Fingerprint || got.MemoryLimitMB != 8192 { t.Fatalf("failed update persisted: %+v %v", got, err) } listed, err := s.ListEnvs(ctx) if err != nil { t.Fatal(err) } for _, env := range listed { if env.MemoryLimitMB < 512 { t.Fatalf("list missing memory: %+v", env) } } }