- Event model + ToJSON - EventSink interface + ChannelEventSink (non-blocking Submit) - event_repo: batch INSERT ON CONFLICT DO NOTHING dedup - event_service: 7-type whitelist + 1KB props limit + ReceivedAt auto-fill - event_flusher: 100/1s batch + sync metric_recent_level_ups on level_up - metric_weekly + metric_upcoming workers (5min/15min with pg_try_advisory_lock) - partitioner: 7-day pre-create + 30-day cleanup (00:05 create / 00:30 cleanup) - 22 unit + integration tests (model/repo/service/sink/worker) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
66 lines
1.7 KiB
Go
66 lines
1.7 KiB
Go
package worker
|
||
|
||
import (
|
||
"context"
|
||
"database/sql"
|
||
"fmt"
|
||
"os"
|
||
"testing"
|
||
|
||
_ "github.com/lib/pq"
|
||
)
|
||
|
||
func setupMaterializerDB(t *testing.T) (*sql.DB, string, func()) {
|
||
dsn := os.Getenv("TEST_DATABASE_URL")
|
||
if dsn == "" {
|
||
t.Skip("TEST_DATABASE_URL not set")
|
||
}
|
||
db, err := sql.Open("postgres", dsn)
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if err := db.Ping(); err != nil {
|
||
t.Skipf("DB ping failed: %v", err)
|
||
}
|
||
schema := "statistic_test_mat_" + sanitizeName(t.Name())
|
||
db.Exec("CREATE SCHEMA IF NOT EXISTS " + schema)
|
||
// refresh_log 表(MV DDL 不需要建,因为只测 RefreshOne 对 refresh_log 的写)
|
||
db.Exec(fmt.Sprintf(`CREATE TABLE IF NOT EXISTS %s.refresh_log (
|
||
id BIGSERIAL PRIMARY KEY,
|
||
mv_name VARCHAR(128) NOT NULL,
|
||
started_at TIMESTAMPTZ NOT NULL,
|
||
finished_at TIMESTAMPTZ,
|
||
row_count BIGINT,
|
||
status VARCHAR(16) NOT NULL,
|
||
error_message TEXT
|
||
)`, schema))
|
||
|
||
cleanup := func() {
|
||
db.Exec("DROP SCHEMA IF EXISTS " + schema + " CASCADE")
|
||
db.Close()
|
||
}
|
||
return db, schema, cleanup
|
||
}
|
||
|
||
func TestMaterializer_RefreshOne_LogsToRefreshLog(t *testing.T) {
|
||
db, schema, cleanup := setupMaterializerDB(t)
|
||
defer cleanup()
|
||
|
||
m := NewMaterializer(db, schema)
|
||
// 用一个不存在的 MV 名(期望失败但 refresh_log 仍写入)
|
||
err := m.RefreshOne(context.Background(), "mv_does_not_exist")
|
||
if err == nil {
|
||
t.Fatal("expected error for non-existent MV")
|
||
}
|
||
// 验证 refresh_log 有 failed 记录
|
||
var status string
|
||
if err := db.QueryRow(
|
||
"SELECT status FROM "+schema+`.refresh_log WHERE mv_name='mv_does_not_exist' ORDER BY id DESC LIMIT 1`,
|
||
).Scan(&status); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if status != "failed" {
|
||
t.Fatalf("expected status=failed, got %s", status)
|
||
}
|
||
}
|