把工程根目录中和"配置加载"、"数据存储"两个领域相关的全部 Go 文件迁到 internal/ 下的子包,按功能分组的第一阶段成果。 internal/config/ - 从 config.go 抽出 Config / MQTTConfig / WebConfig / DatabaseConfig 等 类型并大写导出,函数改名 Default/Load/Write/Validate/BuildTLS 等。 - 测试随被测代码迁移成 package config 的内部测试。 - 根目录留 config.go 作桥接:用 type alias 让旧的小写名(config / mqttConfig / databaseConfig 等)继续可用,避免修改 30+ 处调用方。 internal/store/ - 把 db.go、store_query.go、db_write_queue.go 与 13 个 *_store.go 一并 迁入;26 个 *Record 类型与 store 同包以避免循环依赖。 - store -> Store;50+ 标识符从小写未导出改为大写导出(包括 record、 ListOptions、错误变量、bot/llm/runtime 常量、helpers 等)。 - 新增 DB() / Driver() 访问器供 ai 子系统使用,避免直接访问私有字段。 - bot_pki_store.go 独立出来,把 PKI 解密所需的 store 方法集中归类。 - helpers.go 提供 hashPassword / uint32FromRecord / printJSON 等以前在 其他根目录文件中的辅助;test_helpers_test.go 提供 verifyPassword 与 publicMapTileSourceDTO 让测试可以本地运行而不依赖 main 包。 根目录新增: - store_bridge.go:完整 type-alias / 函数包装层,把 internal/store 的 导出名映射回旧的小写名,让 admin_*_routes.go、web.go、bot_service.go 等仍未迁出的文件继续编译。后续步骤把它们迁到各自领域包后可逐步删除。 - test_helpers_test.go:根目录测试沿用 openTestStore 的入口。 go build ./... 与 go test ./... 全部通过;测试数量与重构前一致。 Co-Authored-By: Claude <noreply@anthropic.com>
105 lines
3.2 KiB
Go
105 lines
3.2 KiB
Go
package store
|
|
|
|
import (
|
|
"database/sql"
|
|
"testing"
|
|
)
|
|
|
|
func TestDBWriteQueueWritesRecordsAsync(t *testing.T) {
|
|
st := openTestStore(t)
|
|
defer st.Close()
|
|
|
|
queue := newDBWriteQueue(st)
|
|
record := textMessageTestRecord("queued")
|
|
queue.EnqueueRecord(record, MQTTClientInfo{ClientID: "client-1"})
|
|
record["text"] = "mutated after enqueue"
|
|
queue.Close()
|
|
|
|
var text, clientID string
|
|
if err := rawTestDB(t, st).QueryRow("SELECT text, mqtt_client_id FROM text_message WHERE from_id = ?", "!12345678").Scan(&text, &clientID); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if text != "queued" || clientID != "client-1" {
|
|
t.Fatalf("queued row = text %q client %q, want queued/client-1", text, clientID)
|
|
}
|
|
}
|
|
|
|
func TestDBWriteQueueWritesDiscardAsync(t *testing.T) {
|
|
st := openTestStore(t)
|
|
defer st.Close()
|
|
|
|
queue := newDBWriteQueue(st)
|
|
record := map[string]any{"topic": "msh/test", "error": "bad packet"}
|
|
queue.EnqueueDiscard(record, []byte{1, 2, 3}, MQTTClientInfo{RemoteAddr: "127.0.0.1:1883"})
|
|
record["error"] = "mutated after enqueue"
|
|
queue.Close()
|
|
|
|
var topic, reason, rawBase64, remoteAddr string
|
|
if err := rawTestDB(t, st).QueryRow("SELECT topic, error, raw_base64, mqtt_remote_addr FROM discard_details").Scan(&topic, &reason, &rawBase64, &remoteAddr); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if topic != "msh/test" || reason != "bad packet" || rawBase64 != "AQID" || remoteAddr != "127.0.0.1:1883" {
|
|
t.Fatalf("discard row = %q/%q/%q/%q, want queued values", topic, reason, rawBase64, remoteAddr)
|
|
}
|
|
}
|
|
|
|
func TestDBWriteQueueLen(t *testing.T) {
|
|
queue := &WriteQueue{jobs: make(chan writeJob, 1)}
|
|
queue.enqueue(writeJob{run: func() error { return nil }})
|
|
if queue.Len() != 1 {
|
|
t.Fatalf("queue.Len() = %d, want 1", queue.Len())
|
|
}
|
|
}
|
|
|
|
func TestDBWriteQueueIgnoresUnsupportedRecordType(t *testing.T) {
|
|
st := openTestStore(t)
|
|
defer st.Close()
|
|
|
|
queue := newDBWriteQueue(st)
|
|
queue.EnqueueRecord(map[string]any{"type": "empty_packet", "from": "!12345678"}, MQTTClientInfo{})
|
|
queue.Close()
|
|
|
|
var count int
|
|
if err := rawTestDB(t, st).QueryRow("SELECT COUNT(*) FROM text_message").Scan(&count); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if count != 0 {
|
|
t.Fatalf("text_message count = %d, want 0", count)
|
|
}
|
|
}
|
|
|
|
func TestDBWriteQueueNilStore(t *testing.T) {
|
|
if queue := newDBWriteQueue(nil); queue != nil {
|
|
t.Fatalf("newDBWriteQueue(nil) = %#v, want nil", queue)
|
|
}
|
|
var queue *WriteQueue
|
|
queue.EnqueueRecord(textMessageTestRecord("ignored"), MQTTClientInfo{})
|
|
queue.EnqueueDiscard(map[string]any{"topic": "ignored"}, []byte{1}, MQTTClientInfo{})
|
|
queue.Close()
|
|
}
|
|
|
|
func TestDBWriteQueueRecordValidationErrorDoesNotStopWorker(t *testing.T) {
|
|
st := openTestStore(t)
|
|
defer st.Close()
|
|
|
|
queue := newDBWriteQueue(st)
|
|
badRecord := textMessageTestRecord("bad")
|
|
delete(badRecord, "from")
|
|
queue.EnqueueRecord(badRecord, MQTTClientInfo{})
|
|
queue.EnqueueRecord(textMessageTestRecord("good"), MQTTClientInfo{})
|
|
queue.Close()
|
|
|
|
var text string
|
|
if err := rawTestDB(t, st).QueryRow("SELECT text FROM text_message").Scan(&text); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if text != "good" {
|
|
t.Fatalf("text = %q, want good", text)
|
|
}
|
|
|
|
var missing sql.NullString
|
|
if err := rawTestDB(t, st).QueryRow("SELECT text FROM text_message WHERE text = ?", "bad").Scan(&missing); err != sql.ErrNoRows {
|
|
t.Fatalf("bad row error = %v, want sql.ErrNoRows", err)
|
|
}
|
|
}
|