From fd767b23c3c952bcd5bc801a2578ef5e91aa2edb Mon Sep 17 00:00:00 2001 From: xuans Date: Mon, 28 Sep 2026 20:58:27 +0800 Subject: [PATCH] =?UTF-8?q?=E6=A3=80=E6=9F=A5=E7=82=B9=EF=BC=9A=E4=BF=9D?= =?UTF-8?q?=E7=95=99=E9=A1=B9=E7=9B=AE=E6=A0=87=E8=AF=86=E5=A4=B1=E8=B4=A5?= =?UTF-8?q?=E7=94=A8=E4=BE=8B=E4=B8=8E=E9=9A=94=E7=A6=BB=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=BA=93=E5=A4=B9=E5=85=B7=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../httpapi/project_lifecycle_api_test.go | 3 +++ .../repository/project_lifecycle_test.go | 18 +++++++++++++----- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/backend/internal/httpapi/project_lifecycle_api_test.go b/backend/internal/httpapi/project_lifecycle_api_test.go index 05f4335..6c9b38d 100644 --- a/backend/internal/httpapi/project_lifecycle_api_test.go +++ b/backend/internal/httpapi/project_lifecycle_api_test.go @@ -102,6 +102,9 @@ func TestProjectLifecycleAPIRejectsInvalidInput(t *testing.T) { {http.MethodPost, "", `{"name":"` + strings.Repeat("a", 129) + `"}`}, {http.MethodPut, "/project-b", `{"name":"名称","owner_id":"forged"}`}, {http.MethodPut, "/project-b?ignored=1", `{"name":"名称"}`}, + {http.MethodPut, "/UPPERCASE", `{"name":"名称"}`}, + {http.MethodPut, "/" + strings.Repeat("a", 37), `{"name":"名称"}`}, + {http.MethodDelete, "/UPPERCASE", `{"confirmation":"UPPERCASE"}`}, {http.MethodPut, "/project-b", strings.Repeat("x", 70*1024)}, {http.MethodDelete, "/project-b", `{}`}, {http.MethodDelete, "/project-b", `{"confirmation":"project-a"}`}, diff --git a/backend/internal/repository/project_lifecycle_test.go b/backend/internal/repository/project_lifecycle_test.go index 9170f56..af3edcf 100644 --- a/backend/internal/repository/project_lifecycle_test.go +++ b/backend/internal/repository/project_lifecycle_test.go @@ -20,15 +20,15 @@ func TestProjectLifecycleSQLite(t *testing.T) { cfg := config.Default(t.TempDir()).Database cfg.DSN = filepath.Join(t.TempDir(), "project-lifecycle.db") return cfg - }) + }, nil) } func TestProjectLifecycleMySQLReal(t *testing.T) { server := mysqltest.New(t) - checkProjectLifecycle(t, server.Database) + checkProjectLifecycle(t, server.Database, server.Admin()) } -func checkProjectLifecycle(t *testing.T, makeConfig func(*testing.T) config.DatabaseConfig) { +func checkProjectLifecycle(t *testing.T, makeConfig func(*testing.T) config.DatabaseConfig, admin *sql.DB) { t.Helper() run := func(name string, check func(*testing.T, *sql.DB, *SQLProjectMemberRepository, string)) { t.Run(name, func(t *testing.T) { @@ -191,11 +191,19 @@ func checkProjectLifecycle(t *testing.T, makeConfig func(*testing.T) config.Data if err != nil { t.Fatal(err) } + executor := db trigger := `CREATE TRIGGER fail_project_audit BEFORE INSERT ON audit_logs BEGIN SELECT RAISE(ABORT, 'audit-failure'); END` if driver == "mysql" { - trigger = `CREATE TRIGGER fail_project_audit BEFORE INSERT ON audit_logs FOR EACH ROW SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='audit-failure'` + // 故障注入由隔离实例管理员执行,不提高业务权限或放宽二进制日志策略。 + var schema string + if err := db.QueryRowContext(t.Context(), "SELECT DATABASE()").Scan(&schema); err != nil { + t.Fatal(err) + } + schema = "`" + strings.ReplaceAll(schema, "`", "``") + "`" + executor = admin + trigger = `CREATE TRIGGER ` + schema + `.fail_project_audit BEFORE INSERT ON ` + schema + `.audit_logs FOR EACH ROW SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='audit-failure'` } - if _, err := db.ExecContext(t.Context(), trigger); err != nil { + if _, err := executor.ExecContext(t.Context(), trigger); err != nil { t.Fatal(err) } switch action {