chore(release): 发布 v2.0.0
CI / verify (push) Canceled after 0s

- 内置 SSH 终端(/terminal):复用已保存的 SSH 密码/私钥与独立 known_hosts 校验
- 终端二次认证、会话审计(/terminal/audits)、录像审计与常用命令
- DDNS 服务商名称与自定义 IP,新增迁移 037/038/039
- CHANGELOG 与 VERSION 更新至 2.0.0
This commit is contained in:
2026-10-16 20:18:04 +08:00
parent a0cc553d2d
commit 302248a907
65 changed files with 7263 additions and 606 deletions
+77
View File
@@ -0,0 +1,77 @@
package terminal
import (
"context"
"errors"
"path/filepath"
"strings"
"testing"
"github.com/havline/havline/internal/db"
)
// TestCommandStoreRoundTrip:保存、列表、删除快捷命令,并校验输入约束。
func TestCommandStoreRoundTrip(t *testing.T) {
conn, err := db.Open(filepath.Join(t.TempDir(), "commands.db"))
if err != nil {
t.Fatalf("打开数据库失败:%v", err)
}
defer conn.Close()
if err := db.Migrate(context.Background(), conn, filepath.Join("..", "..", "migrations")); err != nil {
t.Fatalf("迁移失败:%v", err)
}
store := NewCommandStore(conn)
ctx := context.Background()
first, err := store.CreateCommand(ctx, " 磁盘占用 ", "df -h\n")
if err != nil {
t.Fatalf("保存命令失败:%v", err)
}
if first.Name != "磁盘占用" {
t.Fatalf("名称应当去掉首尾空格:%q", first.Name)
}
if first.Command != "df -h" {
t.Fatalf("命令应当去掉尾部换行:%q", first.Command)
}
if first.ID == 0 || first.CreatedAt == "" {
t.Fatalf("保存结果缺少 ID 或时间:%+v", first)
}
if _, err := store.CreateCommand(ctx, "systemctl", "systemctl status nginx"); err != nil {
t.Fatalf("保存第二条失败:%v", err)
}
list, err := store.ListCommands(ctx)
if err != nil {
t.Fatalf("列表失败:%v", err)
}
if len(list) != 2 {
t.Fatalf("期望 2 条,实际 %d 条", len(list))
}
// 倒序:后加的在前
if list[0].Name != "systemctl" {
t.Fatalf("排序不符:%+v", list)
}
if err := store.DeleteCommand(ctx, first.ID); err != nil {
t.Fatalf("删除失败:%v", err)
}
if err := store.DeleteCommand(ctx, first.ID); !errors.Is(err, ErrCommandNotFound) {
t.Fatalf("重复删除应当 ErrCommandNotFound,实际 %v", err)
}
rest, err := store.ListCommands(ctx)
if err != nil || len(rest) != 1 {
t.Fatalf("删除后应当剩 1 条:%v %d", err, len(rest))
}
// 输入约束
if _, err := store.CreateCommand(ctx, " ", "ls"); !errors.Is(err, ErrInvalidCommand) {
t.Fatalf("空名称应当报错,实际 %v", err)
}
if _, err := store.CreateCommand(ctx, "空白命令", " "); !errors.Is(err, ErrInvalidCommand) {
t.Fatalf("空命令应当报错,实际 %v", err)
}
if _, err := store.CreateCommand(ctx, strings.Repeat("长", maxCommandNameLength+1), "ls"); err == nil {
t.Fatal("超长名称应当报错")
}
}