mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-08-21 08:26:20 +00:00
- Consolidated PG migration 054: agent_hooks rename + junction table + deprecated column drop - Updated SQLite schema v19 with final consolidated migration - Removed obsolete schema rebuild files (v21, v23) - Updated Go store layer (pg/hooks.go, sqlitestore/hooks.go) - Updated integration tests to use new table names (hooks, hook_agents) - Updated TypeScript protocol, UI components, and i18n strings - Updated gateway methods to reflect schema changes Tables: agent_hooks → hooks, agent_hook_agents → hook_agents
117 lines
4.2 KiB
Go
117 lines
4.2 KiB
Go
//go:build integration
|
|
|
|
package integration
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
|
|
"github.com/nextlevelbuilder/goclaw/internal/edition"
|
|
"github.com/nextlevelbuilder/goclaw/internal/hooks"
|
|
"github.com/nextlevelbuilder/goclaw/internal/store"
|
|
)
|
|
|
|
// ─── Phase 08 shared test helpers ────────────────────────────────────────────
|
|
//
|
|
// Helpers scoped to the Phase 08 hook integration tests. Do NOT extend with
|
|
// generic store / channel helpers — keep additions hook-specific so the file
|
|
// stays focused.
|
|
|
|
// withEdition flips edition.Current() for the duration of the test, restoring
|
|
// the prior value on cleanup. Used by tests that exercise the edition-gated
|
|
// migration + validation paths (E bucket, F5).
|
|
func withEdition(t *testing.T, e edition.Edition) {
|
|
t.Helper()
|
|
prev := edition.Current()
|
|
edition.SetCurrent(e)
|
|
t.Cleanup(func() { edition.SetCurrent(prev) })
|
|
}
|
|
|
|
// seedExtraTenant inserts a tenant row with auto-cleanup. Returns the new id.
|
|
// Used by tests that need a SECOND tenant alongside seedTenantAgent's pair so
|
|
// we can assert isolation between two real tenants without re-running the
|
|
// full agent fixture chain.
|
|
func seedExtraTenant(t *testing.T, db *sql.DB) uuid.UUID {
|
|
t.Helper()
|
|
id := uuid.New()
|
|
_, err := db.Exec(
|
|
`INSERT INTO tenants (id, name, slug, status) VALUES ($1, $2, $3, 'active')
|
|
ON CONFLICT DO NOTHING`,
|
|
id, "test-tenant-"+id.String()[:8], "t"+id.String()[:8])
|
|
if err != nil {
|
|
t.Fatalf("seed extra tenant: %v", err)
|
|
}
|
|
t.Cleanup(func() { db.Exec("DELETE FROM tenants WHERE id = $1", id) })
|
|
return id
|
|
}
|
|
|
|
// purgeAgentHooksByTenant cleans hook + execution rows for one tenant. Used by
|
|
// the seed-reconciliation tests that re-run Seed() and need a clean slate.
|
|
func purgeAgentHooksByTenant(t *testing.T, db *sql.DB, tenantID uuid.UUID) {
|
|
t.Helper()
|
|
db.Exec(`DELETE FROM hook_executions WHERE hook_id IN (SELECT id FROM hooks WHERE tenant_id = $1)`, tenantID)
|
|
db.Exec(`DELETE FROM hooks WHERE tenant_id = $1`, tenantID)
|
|
}
|
|
|
|
// purgeAgentHookByID removes one hook row + its audit. Standard cleanup
|
|
// pattern for tests that create a single hook explicitly.
|
|
func purgeAgentHookByID(t *testing.T, db *sql.DB, id uuid.UUID) {
|
|
t.Helper()
|
|
db.Exec(`DELETE FROM hook_executions WHERE hook_id = $1`, id)
|
|
db.Exec(`DELETE FROM hooks WHERE id = $1`, id)
|
|
}
|
|
|
|
// pollAuditDecision polls hook_executions for one row matching (hookID,
|
|
// decision) — returns true once observed or false on timeout. Stricter
|
|
// equivalent of pollAuditCount(want=1) when callers want a boolean check.
|
|
func pollAuditDecision(t *testing.T, db *sql.DB, hookID uuid.UUID, decision string, timeout time.Duration) bool {
|
|
t.Helper()
|
|
deadline := time.Now().Add(timeout)
|
|
for time.Now().Before(deadline) {
|
|
var n int
|
|
if err := db.QueryRow(
|
|
`SELECT COUNT(*) FROM hook_executions WHERE hook_id = $1 AND decision = $2`,
|
|
hookID, decision,
|
|
).Scan(&n); err == nil && n > 0 {
|
|
return true
|
|
}
|
|
time.Sleep(20 * time.Millisecond)
|
|
}
|
|
return false
|
|
}
|
|
|
|
// crossTenantOwnerCtx returns a context bypassing tenant scope AND carrying
|
|
// the owner role — required for tests that reach into rows across tenants
|
|
// (e.g. seed reconciliation, command-autodisable migration which lists
|
|
// every tenant's enabled rows).
|
|
func crossTenantOwnerCtx() context.Context {
|
|
ctx := store.WithTenantID(context.Background(), store.MasterTenantID)
|
|
ctx = store.WithCrossTenant(ctx)
|
|
ctx = store.WithRole(ctx, store.RoleOwner)
|
|
return ctx
|
|
}
|
|
|
|
// hookCfgScript returns a baseline script hook config. Tests override fields
|
|
// (scope, source, event, etc.) before passing to Create. Centralizes the
|
|
// boilerplate so each test's intent stays one-line-clear.
|
|
func hookCfgScript(tenantID uuid.UUID, ev hooks.HookEvent, source string, src string) hooks.HookConfig {
|
|
return hooks.HookConfig{
|
|
TenantID: tenantID,
|
|
Scope: hooks.ScopeTenant,
|
|
Event: ev,
|
|
HandlerType: hooks.HandlerScript,
|
|
Config: map[string]any{"source": src},
|
|
TimeoutMS: 2000,
|
|
OnTimeout: hooks.DecisionAllow,
|
|
Priority: 100,
|
|
Enabled: true,
|
|
Version: 1,
|
|
Source: source,
|
|
Metadata: map[string]any{},
|
|
}
|
|
}
|