Files
MyGoNavi/internal/app/sql_import_preflight_test.go
Syngnat 390a249105 feat(data-import): 重构大文件导入与安全任务运行时
- SQL、CSV、JSON 与 XLSX 改为资源受限的流式解析,并支持压缩、编码和方言预检

- 增加遇错停止/继续策略、持久任务、错误行导出、源文件身份与安全取消

- 为数据库驱动补齐上下文事务和写入结果未知语义,避免失败重放与脏会话复用
2026-08-08 20:51:23 +08:00

135 lines
5.0 KiB
Go

package app
import (
"strings"
"testing"
)
func TestPreflightSQLImportRejectsPostgresCopyFromStdin(t *testing.T) {
result, err := PreflightSQLImport(strings.NewReader("COPY public.demo (id, name) FROM STDIN;\n1\talpha\n\\.\n"), "postgres")
if err != nil {
t.Fatalf("preflight SQL import: %v", err)
}
if result.Safe || result.Reason == nil {
t.Fatalf("result = %#v, want a structured rejection", result)
}
if result.Reason.Code != SQLImportPreflightPostgresCopyFromStdin {
t.Fatalf("reason code = %q, want %q", result.Reason.Code, SQLImportPreflightPostgresCopyFromStdin)
}
}
func TestPreflightSQLImportRejectsPsqlMetaCommand(t *testing.T) {
result, err := PreflightSQLImport(strings.NewReader("\\connect reporting\nSELECT 1;\n"), "postgres")
if err != nil {
t.Fatalf("preflight SQL import: %v", err)
}
if result.Safe || result.Reason == nil {
t.Fatalf("result = %#v, want a structured rejection", result)
}
if result.Reason.Code != SQLImportPreflightReasonCode("psql_meta_command") {
t.Fatalf("reason code = %q, want psql_meta_command", result.Reason.Code)
}
}
func TestPreflightSQLImportRejectsInlinePsqlMetaCommand(t *testing.T) {
result, err := PreflightSQLImport(strings.NewReader("SELECT 1 \\g\n"), "postgres")
if err != nil {
t.Fatalf("preflight SQL import: %v", err)
}
if result.Safe || result.Reason == nil || result.Reason.Code != SQLImportPreflightPsqlMetaCommand {
t.Fatalf("result = %#v, want inline psql meta-command rejection", result)
}
}
func TestPreflightSQLImportRejectsSQLCmdInclude(t *testing.T) {
result, err := PreflightSQLImport(strings.NewReader(":r child.sql\nSELECT 1;\n"), "sqlserver")
if err != nil {
t.Fatalf("preflight SQL import: %v", err)
}
if result.Safe || result.Reason == nil {
t.Fatalf("result = %#v, want a structured rejection", result)
}
if result.Reason.Code != SQLImportPreflightReasonCode("sqlcmd_command") {
t.Fatalf("reason code = %q, want sqlcmd_command", result.Reason.Code)
}
}
func TestPreflightSQLImportRejectsMySQLSourceCommand(t *testing.T) {
result, err := PreflightSQLImport(strings.NewReader("SOURCE child.sql;\n"), "mysql")
if err != nil {
t.Fatalf("preflight SQL import: %v", err)
}
if result.Safe || result.Reason == nil {
t.Fatalf("result = %#v, want a structured rejection", result)
}
if result.Reason.Code != SQLImportPreflightReasonCode("mysql_client_command") {
t.Fatalf("reason code = %q, want mysql_client_command", result.Reason.Code)
}
}
func TestPreflightSQLImportRejectsInlineMySQLClientCommand(t *testing.T) {
result, err := PreflightSQLImport(strings.NewReader("SELECT 1 \\G\n"), "mysql")
if err != nil {
t.Fatalf("preflight SQL import: %v", err)
}
if result.Safe || result.Reason == nil || result.Reason.Code != SQLImportPreflightMySQLClientCommand {
t.Fatalf("result = %#v, want inline mysql client-command rejection", result)
}
}
func TestPreflightSQLImportRejectsSQLPlusInclude(t *testing.T) {
result, err := PreflightSQLImport(strings.NewReader("@child.sql\nSELECT 1 FROM dual;\n"), "oracle")
if err != nil {
t.Fatalf("preflight SQL import: %v", err)
}
if result.Safe || result.Reason == nil {
t.Fatalf("result = %#v, want a structured rejection", result)
}
if result.Reason.Code != SQLImportPreflightReasonCode("sqlplus_command") {
t.Fatalf("reason code = %q, want sqlplus_command", result.Reason.Code)
}
if result.Reason.Directive != "@" {
t.Fatalf("directive = %q, want redacted include marker", result.Reason.Directive)
}
}
func TestPreflightSQLImportRejectsSQLiteDotCommand(t *testing.T) {
result, err := PreflightSQLImport(strings.NewReader(".read child.sql\nSELECT 1;\n"), "sqlite")
if err != nil {
t.Fatalf("preflight SQL import: %v", err)
}
if result.Safe || result.Reason == nil || result.Reason.Code != SQLImportPreflightReasonCode("sqlite_client_command") {
t.Fatalf("result = %#v, want sqlite client-command rejection", result)
}
}
func TestPreflightSQLImportIgnoresClientCommandTextInsidePostgresDollarQuote(t *testing.T) {
result, err := PreflightSQLImport(strings.NewReader("SELECT $$line one\n\\connect not_a_command\nline three$$;\n"), "postgres")
if err != nil {
t.Fatalf("preflight SQL import: %v", err)
}
if !result.Safe || result.Reason != nil {
t.Fatalf("result = %#v, want quoted client-command text to remain safe", result)
}
}
func TestPreflightSQLImportDoesNotTreatQuotedStdinAsCopyProtocol(t *testing.T) {
result, err := PreflightSQLImport(strings.NewReader("COPY demo FROM 'stdin';\n"), "postgres")
if err != nil {
t.Fatalf("preflight SQL import: %v", err)
}
if !result.Safe || result.Reason != nil {
t.Fatalf("result = %#v, want quoted filename to remain ordinary SQL", result)
}
}
func TestPreflightSQLImportDoesNotTreatCopyQueryTableAsStdinProtocol(t *testing.T) {
result, err := PreflightSQLImport(strings.NewReader("COPY (SELECT * FROM stdin) TO STDOUT;\n"), "postgres")
if err != nil {
t.Fatalf("preflight SQL import: %v", err)
}
if !result.Safe || result.Reason != nil {
t.Fatalf("result = %#v, want COPY query to remain ordinary SQL", result)
}
}