Files
opsxdb/apps/server/internal/db/ddl_test.go
T
logan a9510235bb
CI / Go server (push) Canceled after 0s
CI / Web + core packages (push) Canceled after 0s
CI / Desktop (Tauri) cargo check (push) Canceled after 0s
feat: 18 引擎驱动全家桶 + 驱动管理/离线包 + 设置中心 + 工作台交互打磨
驱动接入(全部本机真实例验收):
- Elasticsearch 原生 SQL API、InfluxDB 原生 HTTP+Flux(官方 JDBC 付费许可,实测弃用)
- ZooKeeper/MongoDB JVM agent(连接时自动下载官方驱动 jar,sha256 钉版本到 ~/.opsxdb/drivers/)
- 达梦 JDBC agent 桥就绪(待厂商 jar+实例);TiDB 真实例验收
- 协议族代码审查放行 PolarDB/TDSQL/OceanBase/Kingbase/GaussDB/Oracle
- 驱动能力模型:按驱动开关对象树分组/右键菜单/行编辑,NoSQL 树拍平(对象直挂库节点)

驱动管理:
- 驱动管理弹框(状态/预装/离线包导入/JDK 检测)+ /api/drivers/* 三接口
- opsxdb-driver 驱动商店仓库(清单/fetch.sh/pack.sh 离线包/import.sh)
- 连接时懒加载:DMG 零 Java 产物,首连自动下载(实测 ZK 九件套 1.35s),离线包自动回退

UI/交互:
- 设置中心(⚙):常规/执行/结果/驱动/快捷键/关于,左导航+右面板(对标 opsxshell)
- 实例级展开箭头(行首 SVG chevron,替代字体回退失真的 Unicode 三角)
- 树内嵌套渲染修复:对象树挂在激活连接下;工具栏窄窗口不断行(按钮 nowrap+整颗换行)
- 侧栏连接行精简(图标+连接名)、SQLite SSH 守卫、Oracle 死参数清理

质量:
- Go 协议层/离线回退单测、E2E 门控测试(ES/ZK/Influx/Mongo/TiDB/JDBC 桥)
- 驱动注册表单一事实来源 connection-types.json,README/BACKLOG/AGENTS 同步
2026-09-22 01:07:46 +08:00

95 lines
2.8 KiB
Go

package db
import (
"context"
"os"
"path/filepath"
"strings"
"testing"
)
// newSQLiteFile 建一个 sqlite 测试库文件并执行建表/插数语句,返回 ConnReq。
func newSQLiteFile(t *testing.T, stmts ...string) ConnReq {
t.Helper()
path := filepath.Join(t.TempDir(), "test.db")
if err := os.WriteFile(path, []byte{}, 0600); err != nil {
t.Fatal(err)
}
for _, s := range stmts {
res := ExecSQL(context.Background(), ConnReq{Driver: "sqlite", Database: path, SQL: s})
if res.Error != "" {
t.Fatalf("初始化语句失败 %q: %s", s, res.Error)
}
}
return ConnReq{Driver: "sqlite", Database: path}
}
// R2-B5:sqlite 返回 sqlite_master 中的原始建表语句。
func TestGetDDLSQLite(t *testing.T) {
c := newSQLiteFile(t, `CREATE TABLE t_person (id INTEGER PRIMARY KEY, name TEXT NOT NULL, age INTEGER)`)
c.Table = "t_person"
ddl, err := GetDDL(context.Background(), c)
if err != nil {
t.Fatal(err)
}
if !strings.Contains(ddl, "CREATE TABLE t_person") || !strings.Contains(ddl, "name TEXT") {
t.Fatalf("DDL 内容不符: %s", ddl)
}
c.Table = "no_such_table"
if _, err := GetDDL(context.Background(), c); err == nil {
t.Fatal("不存在的表应报错")
}
}
func TestGetDDLRedis(t *testing.T) {
_, err := GetDDL(context.Background(), ConnReq{Driver: "redis", Table: "k"})
if err == nil || !strings.Contains(err.Error(), "不支持") {
t.Fatalf("redis 应报不支持: %v", err)
}
}
func TestGetDDLMissingTable(t *testing.T) {
_, err := GetDDL(context.Background(), ConnReq{Driver: "sqlite", Table: ""})
if err == nil {
t.Fatal("空表名应报错")
}
}
// R2-B5:pg/mssql/oracle 族用列元数据拼装,各 family 引号不同。
func TestBuildDDLFromColumns(t *testing.T) {
cols := []ColumnInfo{
{Name: "id", Type: "integer", Nullable: false, Key: "PRI"},
{Name: "na\"me", Type: "text", Nullable: true},
}
// pg:双引号加倍
ddl, err := buildDDLFromColumns("postgres", "public.t", cols)
if err != nil {
t.Fatal(err)
}
if !strings.Contains(ddl, `"public"."t"`) || !strings.Contains(ddl, `"na""me" text`) ||
!strings.Contains(ddl, `"id" integer NOT NULL,`) || !strings.Contains(ddl, `PRIMARY KEY ("id")`) {
t.Fatalf("pg DDL 不符:\n%s", ddl)
}
// mssql:中括号
ddl, err = buildDDLFromColumns("mssql", "dbo.t", cols)
if err != nil {
t.Fatal(err)
}
if !strings.Contains(ddl, `[id] integer NOT NULL`) || !strings.Contains(ddl, `PRIMARY KEY ([id])`) {
t.Fatalf("mssql DDL 不符:\n%s", ddl)
}
// oracle:原名
ddl, err = buildDDLFromColumns("oracle", "T", cols)
if err != nil {
t.Fatal(err)
}
if !strings.Contains(ddl, `CREATE TABLE T (`) || !strings.Contains(ddl, `PRIMARY KEY (id)`) {
t.Fatalf("oracle DDL 不符:\n%s", ddl)
}
// 无列报错
if _, err := buildDDLFromColumns("postgres", "t", nil); err == nil {
t.Fatal("无列应报错")
}
}