删除所有测试文件
This commit is contained in:
@@ -1,207 +0,0 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func TestNodeBlockingCRUD(t *testing.T) {
|
||||
st := openTestStore(t)
|
||||
defer st.Close()
|
||||
|
||||
nodeNum := int64(305419896)
|
||||
rule, err := st.CreateNodeBlocking(" !12345678 ", &nodeNum, " noisy node ", true)
|
||||
if err != nil {
|
||||
t.Fatalf("CreateNodeBlocking() error = %v", err)
|
||||
}
|
||||
if rule.NodeID != "!12345678" || rule.NodeNum == nil || *rule.NodeNum != nodeNum || rule.Reason != "noisy node" || !rule.Enabled {
|
||||
t.Fatalf("created node rule = %+v, want normalized fields", rule)
|
||||
}
|
||||
|
||||
if _, err := st.CreateNodeBlocking("!12345678", nil, "duplicate", true); !errors.Is(err, ErrBlockingAlreadyExists) {
|
||||
t.Fatalf("duplicate CreateNodeBlocking() error = %v, want ErrBlockingAlreadyExists", err)
|
||||
}
|
||||
|
||||
updatedNum := int64(7)
|
||||
updated, err := st.UpdateNodeBlocking(rule.ID, "!00000007", &updatedNum, "updated", false)
|
||||
if err != nil {
|
||||
t.Fatalf("UpdateNodeBlocking() error = %v", err)
|
||||
}
|
||||
if updated.NodeID != "!00000007" || updated.NodeNum == nil || *updated.NodeNum != updatedNum || updated.Reason != "updated" || updated.Enabled {
|
||||
t.Fatalf("updated node rule = %+v, want updated fields", updated)
|
||||
}
|
||||
|
||||
rows, err := st.ListNodeBlocking(ListOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("ListNodeBlocking() error = %v", err)
|
||||
}
|
||||
if len(rows) != 1 || rows[0].ID != rule.ID {
|
||||
t.Fatalf("ListNodeBlocking() = %+v, want one updated rule", rows)
|
||||
}
|
||||
total, err := st.CountNodeBlocking(ListOptions{})
|
||||
if err != nil || total != 1 {
|
||||
t.Fatalf("CountNodeBlocking() = %d, %v, want 1, nil", total, err)
|
||||
}
|
||||
|
||||
if err := st.DeleteNodeBlocking(rule.ID); err != nil {
|
||||
t.Fatalf("DeleteNodeBlocking() error = %v", err)
|
||||
}
|
||||
if err := st.DeleteNodeBlocking(rule.ID); !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
t.Fatalf("DeleteNodeBlocking(missing) error = %v, want record not found", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNodeBlockingValidation(t *testing.T) {
|
||||
st := openTestStore(t)
|
||||
defer st.Close()
|
||||
|
||||
if _, err := st.CreateNodeBlocking(" ", nil, "", true); err == nil {
|
||||
t.Fatal("CreateNodeBlocking(empty) error = nil, want error")
|
||||
}
|
||||
if _, err := st.UpdateNodeBlocking(1, "!missing", nil, "", true); !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
t.Fatalf("UpdateNodeBlocking(missing) error = %v, want record not found", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIPBlockingCRUDAndValidation(t *testing.T) {
|
||||
st := openTestStore(t)
|
||||
defer st.Close()
|
||||
|
||||
rule, err := st.CreateIPBlocking(" 127.0.0.1 ", "local", true)
|
||||
if err != nil {
|
||||
t.Fatalf("CreateIPBlocking(ip) error = %v", err)
|
||||
}
|
||||
if rule.IPValue != "127.0.0.1" || rule.Reason != "local" || !rule.Enabled {
|
||||
t.Fatalf("created ip rule = %+v, want normalized IP", rule)
|
||||
}
|
||||
|
||||
cidr, err := st.CreateIPBlocking("192.168.1.99/24", "cidr", true)
|
||||
if err != nil {
|
||||
t.Fatalf("CreateIPBlocking(cidr) error = %v", err)
|
||||
}
|
||||
if cidr.IPValue != "192.168.1.0/24" {
|
||||
t.Fatalf("cidr IPValue = %q, want 192.168.1.0/24", cidr.IPValue)
|
||||
}
|
||||
|
||||
if _, err := st.CreateIPBlocking("127.0.0.1", "duplicate", true); !errors.Is(err, ErrBlockingAlreadyExists) {
|
||||
t.Fatalf("duplicate CreateIPBlocking() error = %v, want ErrBlockingAlreadyExists", err)
|
||||
}
|
||||
if _, err := st.CreateIPBlocking("not-an-ip", "invalid", true); err == nil {
|
||||
t.Fatal("CreateIPBlocking(invalid) error = nil, want error")
|
||||
}
|
||||
|
||||
updated, err := st.UpdateIPBlocking(rule.ID, "10.0.0.0/8", "updated", false)
|
||||
if err != nil {
|
||||
t.Fatalf("UpdateIPBlocking() error = %v", err)
|
||||
}
|
||||
if updated.IPValue != "10.0.0.0/8" || updated.Reason != "updated" || updated.Enabled {
|
||||
t.Fatalf("updated ip rule = %+v, want updated fields", updated)
|
||||
}
|
||||
|
||||
rows, err := st.ListIPBlocking(ListOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("ListIPBlocking() error = %v", err)
|
||||
}
|
||||
if len(rows) != 2 {
|
||||
t.Fatalf("ListIPBlocking() length = %d, want 2", len(rows))
|
||||
}
|
||||
total, err := st.CountIPBlocking(ListOptions{})
|
||||
if err != nil || total != 2 {
|
||||
t.Fatalf("CountIPBlocking() = %d, %v, want 2, nil", total, err)
|
||||
}
|
||||
|
||||
if err := st.DeleteIPBlocking(rule.ID); err != nil {
|
||||
t.Fatalf("DeleteIPBlocking() error = %v", err)
|
||||
}
|
||||
if err := st.DeleteIPBlocking(rule.ID); !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
t.Fatalf("DeleteIPBlocking(missing) error = %v, want record not found", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestListEnabledBlockingRules(t *testing.T) {
|
||||
st := openTestStore(t)
|
||||
defer st.Close()
|
||||
|
||||
nodeNum := int64(1)
|
||||
if _, err := st.CreateNodeBlocking("!00000001", &nodeNum, "enabled", true); err != nil {
|
||||
t.Fatalf("CreateNodeBlocking(enabled) error = %v", err)
|
||||
}
|
||||
if _, err := st.CreateNodeBlocking("!00000002", nil, "disabled", false); err != nil {
|
||||
t.Fatalf("CreateNodeBlocking(disabled) error = %v", err)
|
||||
}
|
||||
if rows, err := st.ListEnabledNodeBlocking(); err != nil || len(rows) != 1 || rows[0].NodeID != "!00000001" {
|
||||
t.Fatalf("ListEnabledNodeBlocking() = %+v, %v, want only enabled node", rows, err)
|
||||
}
|
||||
|
||||
if _, err := st.CreateIPBlocking("127.0.0.1", "enabled", true); err != nil {
|
||||
t.Fatalf("CreateIPBlocking(enabled) error = %v", err)
|
||||
}
|
||||
if _, err := st.CreateIPBlocking("192.168.1.1", "disabled", false); err != nil {
|
||||
t.Fatalf("CreateIPBlocking(disabled) error = %v", err)
|
||||
}
|
||||
if rows, err := st.ListEnabledIPBlocking(); err != nil || len(rows) != 1 || rows[0].IPValue != "127.0.0.1" {
|
||||
t.Fatalf("ListEnabledIPBlocking() = %+v, %v, want only enabled IP", rows, err)
|
||||
}
|
||||
|
||||
if _, err := st.CreateForbiddenWordBlocking("spam", "contains", false, "enabled", true); err != nil {
|
||||
t.Fatalf("CreateForbiddenWordBlocking(enabled) error = %v", err)
|
||||
}
|
||||
if _, err := st.CreateForbiddenWordBlocking("eggs", "contains", false, "disabled", false); err != nil {
|
||||
t.Fatalf("CreateForbiddenWordBlocking(disabled) error = %v", err)
|
||||
}
|
||||
if rows, err := st.ListEnabledForbiddenWordBlocking(); err != nil || len(rows) != 1 || rows[0].Word != "spam" {
|
||||
t.Fatalf("ListEnabledForbiddenWordBlocking() = %+v, %v, want only enabled word", rows, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestForbiddenWordBlockingCRUDAndValidation(t *testing.T) {
|
||||
st := openTestStore(t)
|
||||
defer st.Close()
|
||||
|
||||
rule, err := st.CreateForbiddenWordBlocking(" spam ", "", false, "junk", true)
|
||||
if err != nil {
|
||||
t.Fatalf("CreateForbiddenWordBlocking() error = %v", err)
|
||||
}
|
||||
if rule.Word != "spam" || rule.MatchType != ForbiddenWordMatchContains || rule.CaseSensitive || rule.Reason != "junk" || !rule.Enabled {
|
||||
t.Fatalf("created word rule = %+v, want normalized fields", rule)
|
||||
}
|
||||
|
||||
if _, err := st.CreateForbiddenWordBlocking("spam", "contains", false, "duplicate", true); !errors.Is(err, ErrBlockingAlreadyExists) {
|
||||
t.Fatalf("duplicate CreateForbiddenWordBlocking() error = %v, want ErrBlockingAlreadyExists", err)
|
||||
}
|
||||
if _, err := st.CreateForbiddenWordBlocking(" ", "contains", false, "empty", true); err == nil {
|
||||
t.Fatal("CreateForbiddenWordBlocking(empty) error = nil, want error")
|
||||
}
|
||||
if _, err := st.CreateForbiddenWordBlocking("regex", "regex", false, "unsupported", true); err == nil {
|
||||
t.Fatal("CreateForbiddenWordBlocking(unsupported match type) error = nil, want error")
|
||||
}
|
||||
|
||||
updated, err := st.UpdateForbiddenWordBlocking(rule.ID, "Spam", "contains", true, "updated", false)
|
||||
if err != nil {
|
||||
t.Fatalf("UpdateForbiddenWordBlocking() error = %v", err)
|
||||
}
|
||||
if updated.Word != "Spam" || updated.MatchType != "contains" || !updated.CaseSensitive || updated.Reason != "updated" || updated.Enabled {
|
||||
t.Fatalf("updated word rule = %+v, want updated fields", updated)
|
||||
}
|
||||
|
||||
rows, err := st.ListForbiddenWordBlocking(ListOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("ListForbiddenWordBlocking() error = %v", err)
|
||||
}
|
||||
if len(rows) != 1 || rows[0].ID != rule.ID {
|
||||
t.Fatalf("ListForbiddenWordBlocking() = %+v, want one updated rule", rows)
|
||||
}
|
||||
total, err := st.CountForbiddenWordBlocking(ListOptions{})
|
||||
if err != nil || total != 1 {
|
||||
t.Fatalf("CountForbiddenWordBlocking() = %d, %v, want 1, nil", total, err)
|
||||
}
|
||||
|
||||
if err := st.DeleteForbiddenWordBlocking(rule.ID); err != nil {
|
||||
t.Fatalf("DeleteForbiddenWordBlocking() error = %v", err)
|
||||
}
|
||||
if err := st.DeleteForbiddenWordBlocking(rule.ID); !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
t.Fatalf("DeleteForbiddenWordBlocking(missing) error = %v, want record not found", err)
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,104 +0,0 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -1,258 +0,0 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func TestMapTileSourceDefaultSeeded(t *testing.T) {
|
||||
st := openTestStore(t)
|
||||
defer st.Close()
|
||||
|
||||
row, err := st.GetDefaultMapTileSource()
|
||||
if err != nil {
|
||||
t.Fatalf("GetDefaultMapTileSource() error = %v", err)
|
||||
}
|
||||
if row.Name != defaultMapTileSourceName || row.URLTemplate != defaultMapTileSourceURLTemplate || !row.Enabled || !row.IsDefault {
|
||||
t.Fatalf("default map source = %+v, want built-in default", row)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateMapTileSourceValidation(t *testing.T) {
|
||||
st := openTestStore(t)
|
||||
defer st.Close()
|
||||
|
||||
if _, err := st.CreateMapTileSource(MapTileSourceInput{Name: "bad", URLTemplate: "https://tiles.example.com/{z}/{x}.png", MaxZoom: 19, Enabled: true, ProxyEnabled: true}); err == nil {
|
||||
t.Fatal("CreateMapTileSource() missing placeholder error = nil, want error")
|
||||
}
|
||||
if _, err := st.CreateMapTileSource(MapTileSourceInput{Name: "bad", URLTemplate: "javascript:alert(1)/{z}/{x}/{y}", MaxZoom: 19, Enabled: true, ProxyEnabled: true}); err == nil {
|
||||
t.Fatal("CreateMapTileSource() invalid scheme error = nil, want error")
|
||||
}
|
||||
if _, err := st.CreateMapTileSource(MapTileSourceInput{Name: "bad", URLTemplate: "https://user:pass@tiles.example.com/{z}/{x}/{y}.png", MaxZoom: 19, Enabled: true, ProxyEnabled: true}); err == nil {
|
||||
t.Fatal("CreateMapTileSource() credentials error = nil, want error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestListEnabledMapTileSources(t *testing.T) {
|
||||
st := openTestStore(t)
|
||||
defer st.Close()
|
||||
|
||||
disabled, err := st.CreateMapTileSource(MapTileSourceInput{Name: "Disabled", URLTemplate: "https://disabled.example.com/{z}/{x}/{y}.png", MaxZoom: 18, Enabled: false})
|
||||
if err != nil {
|
||||
t.Fatalf("CreateMapTileSource(disabled) error = %v", err)
|
||||
}
|
||||
custom, err := st.CreateMapTileSource(MapTileSourceInput{Name: "Custom", URLTemplate: "https://custom.example.com/{z}/{x}/{y}.png", MaxZoom: 18, Enabled: true, ProxyEnabled: true})
|
||||
if err != nil {
|
||||
t.Fatalf("CreateMapTileSource(custom) error = %v", err)
|
||||
}
|
||||
if _, err := st.SetDefaultMapTileSource(custom.ID); err != nil {
|
||||
t.Fatalf("SetDefaultMapTileSource() error = %v", err)
|
||||
}
|
||||
|
||||
rows, err := st.ListEnabledMapTileSources()
|
||||
if err != nil {
|
||||
t.Fatalf("ListEnabledMapTileSources() error = %v", err)
|
||||
}
|
||||
if len(rows) < 2 {
|
||||
t.Fatalf("ListEnabledMapTileSources() length = %d, want at least 2", len(rows))
|
||||
}
|
||||
if rows[0].ID != custom.ID {
|
||||
t.Fatalf("first enabled source id = %d, want default %d", rows[0].ID, custom.ID)
|
||||
}
|
||||
for _, row := range rows {
|
||||
if row.ID == disabled.ID {
|
||||
t.Fatalf("disabled source was returned: %+v", row)
|
||||
}
|
||||
if !row.Enabled {
|
||||
t.Fatalf("disabled row returned: %+v", row)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMapTileSourceDuplicateAndDefaultRules(t *testing.T) {
|
||||
st := openTestStore(t)
|
||||
defer st.Close()
|
||||
|
||||
first, err := st.CreateMapTileSource(MapTileSourceInput{Name: "Custom", URLTemplate: "https://tiles.example.com/{z}/{x}/{y}.png", MaxZoom: 18, Enabled: true, ProxyEnabled: true})
|
||||
if err != nil {
|
||||
t.Fatalf("CreateMapTileSource() error = %v", err)
|
||||
}
|
||||
if _, err := st.CreateMapTileSource(MapTileSourceInput{Name: "Custom", URLTemplate: "https://tiles2.example.com/{z}/{x}/{y}.png", MaxZoom: 18, Enabled: true, ProxyEnabled: true}); !errors.Is(err, ErrMapTileSourceAlreadyExists) {
|
||||
t.Fatalf("duplicate name error = %v, want ErrMapTileSourceAlreadyExists", err)
|
||||
}
|
||||
if _, err := st.CreateMapTileSource(MapTileSourceInput{Name: "Custom 2", URLTemplate: first.URLTemplate, MaxZoom: 18, Enabled: true, ProxyEnabled: true}); !errors.Is(err, ErrMapTileSourceAlreadyExists) {
|
||||
t.Fatalf("duplicate url error = %v, want ErrMapTileSourceAlreadyExists", err)
|
||||
}
|
||||
|
||||
updated, err := st.SetDefaultMapTileSource(first.ID)
|
||||
if err != nil {
|
||||
t.Fatalf("SetDefaultMapTileSource() error = %v", err)
|
||||
}
|
||||
if !updated.IsDefault {
|
||||
t.Fatalf("updated default = %+v, want is_default", updated)
|
||||
}
|
||||
|
||||
oldDefault, err := st.GetDefaultMapTileSource()
|
||||
if err != nil {
|
||||
t.Fatalf("GetDefaultMapTileSource() error = %v", err)
|
||||
}
|
||||
if oldDefault.ID != first.ID {
|
||||
t.Fatalf("default id = %d, want %d", oldDefault.ID, first.ID)
|
||||
}
|
||||
if _, err := st.UpdateMapTileSource(first.ID, MapTileSourceInput{Name: first.Name, URLTemplate: first.URLTemplate, Attribution: first.Attribution, MaxZoom: first.MaxZoom, Enabled: false, IsDefault: true}); !errors.Is(err, ErrMapTileSourceCannotDisableDefault) {
|
||||
t.Fatalf("disable default error = %v, want ErrMapTileSourceCannotDisableDefault", err)
|
||||
}
|
||||
if err := st.DeleteMapTileSource(first.ID); !errors.Is(err, ErrMapTileSourceCannotDeleteDefault) {
|
||||
t.Fatalf("delete default error = %v, want ErrMapTileSourceCannotDeleteDefault", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMapTileSourceHashIsSetOnCreate(t *testing.T) {
|
||||
st := openTestStore(t)
|
||||
defer st.Close()
|
||||
|
||||
row, err := st.CreateMapTileSource(MapTileSourceInput{Name: "Hashed", URLTemplate: "https://test.example.com/{z}/{x}/{y}.png", MaxZoom: 18, Enabled: true, ProxyEnabled: true})
|
||||
if err != nil {
|
||||
t.Fatalf("CreateMapTileSource() error = %v", err)
|
||||
}
|
||||
want := MapTileSourceHash("https://test.example.com/{z}/{x}/{y}.png")
|
||||
if row.URLTemplateHash != want {
|
||||
t.Fatalf("URLTemplateHash = %q, want %q", row.URLTemplateHash, want)
|
||||
}
|
||||
if !row.ProxyEnabled {
|
||||
t.Fatal("ProxyEnabled = false, want true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMapTileSourceDefaultHasHash(t *testing.T) {
|
||||
st := openTestStore(t)
|
||||
defer st.Close()
|
||||
|
||||
row, err := st.GetDefaultMapTileSource()
|
||||
if err != nil {
|
||||
t.Fatalf("GetDefaultMapTileSource() error = %v", err)
|
||||
}
|
||||
want := MapTileSourceHash(defaultMapTileSourceURLTemplate)
|
||||
if row.URLTemplateHash != want {
|
||||
t.Fatalf("default URLTemplateHash = %q, want %q", row.URLTemplateHash, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetEnabledMapTileSourceByHash(t *testing.T) {
|
||||
st := openTestStore(t)
|
||||
defer st.Close()
|
||||
|
||||
row, err := st.CreateMapTileSource(MapTileSourceInput{Name: "HashLookup", URLTemplate: "https://lookup.example.com/{z}/{x}/{y}.png", MaxZoom: 18, Enabled: true, ProxyEnabled: true})
|
||||
if err != nil {
|
||||
t.Fatalf("CreateMapTileSource() error = %v", err)
|
||||
}
|
||||
|
||||
found, err := st.GetEnabledMapTileSourceByHash(row.URLTemplateHash)
|
||||
if err != nil {
|
||||
t.Fatalf("GetEnabledMapTileSourceByHash() error = %v", err)
|
||||
}
|
||||
if found.ID != row.ID {
|
||||
t.Fatalf("found ID = %d, want %d", found.ID, row.ID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetEnabledMapTileSourceByHashDisabled(t *testing.T) {
|
||||
st := openTestStore(t)
|
||||
defer st.Close()
|
||||
|
||||
row, err := st.CreateMapTileSource(MapTileSourceInput{Name: "DisabledHash", URLTemplate: "https://disabled-hash.example.com/{z}/{x}/{y}.png", MaxZoom: 18, Enabled: false})
|
||||
if err != nil {
|
||||
t.Fatalf("CreateMapTileSource() error = %v", err)
|
||||
}
|
||||
|
||||
_, err = st.GetEnabledMapTileSourceByHash(row.URLTemplateHash)
|
||||
if !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
t.Fatalf("GetEnabledMapTileSourceByHash(disabled) = %v, want gorm.ErrRecordNotFound", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetEnabledMapTileSourceByHashProxyDisabled(t *testing.T) {
|
||||
st := openTestStore(t)
|
||||
defer st.Close()
|
||||
|
||||
row, err := st.CreateMapTileSource(MapTileSourceInput{Name: "ProxyDisabledHash", URLTemplate: "https://proxy-disabled.example.com/{z}/{x}/{y}.png", MaxZoom: 18, Enabled: true, ProxyEnabled: false})
|
||||
if err != nil {
|
||||
t.Fatalf("CreateMapTileSource() error = %v", err)
|
||||
}
|
||||
|
||||
_, err = st.GetEnabledMapTileSourceByHash(row.URLTemplateHash)
|
||||
if !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
t.Fatalf("GetEnabledMapTileSourceByHash(proxy disabled) = %v, want gorm.ErrRecordNotFound", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetEnabledMapTileSourceByHashUnknown(t *testing.T) {
|
||||
st := openTestStore(t)
|
||||
defer st.Close()
|
||||
|
||||
_, err := st.GetEnabledMapTileSourceByHash("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
|
||||
if !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
t.Fatalf("GetEnabledMapTileSourceByHash(unknown) = %v, want gorm.ErrRecordNotFound", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPublicMapTileSourceDTOProxyURL(t *testing.T) {
|
||||
st := openTestStore(t)
|
||||
defer st.Close()
|
||||
|
||||
row, err := st.CreateMapTileSource(MapTileSourceInput{Name: "ProxyTest", URLTemplate: "https://proxy.example.com/{z}/{x}/{y}.png", MaxZoom: 18, Enabled: true, ProxyEnabled: true})
|
||||
if err != nil {
|
||||
t.Fatalf("CreateMapTileSource() error = %v", err)
|
||||
}
|
||||
|
||||
dto := publicMapTileSourceDTO(*row)
|
||||
urlTemplate, ok := dto["url_template"].(string)
|
||||
if !ok {
|
||||
t.Fatal("url_template is not a string")
|
||||
}
|
||||
wantPrefix := "/api/map/" + row.URLTemplateHash + "?x={x}&y={y}&z={z}"
|
||||
if urlTemplate != wantPrefix {
|
||||
t.Fatalf("url_template = %q, want %q", urlTemplate, wantPrefix)
|
||||
}
|
||||
if strings.Contains(urlTemplate, "proxy.example.com") {
|
||||
t.Fatal("url_template should not contain upstream hostname")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPublicMapTileSourceDTORawURLWhenProxyDisabled(t *testing.T) {
|
||||
st := openTestStore(t)
|
||||
defer st.Close()
|
||||
|
||||
row, err := st.CreateMapTileSource(MapTileSourceInput{Name: "RawTest", URLTemplate: "https://raw.example.com/{z}/{x}/{y}.png", MaxZoom: 18, Enabled: true, ProxyEnabled: false})
|
||||
if err != nil {
|
||||
t.Fatalf("CreateMapTileSource() error = %v", err)
|
||||
}
|
||||
|
||||
dto := publicMapTileSourceDTO(*row)
|
||||
urlTemplate, ok := dto["url_template"].(string)
|
||||
if !ok {
|
||||
t.Fatal("url_template is not a string")
|
||||
}
|
||||
if urlTemplate != row.URLTemplate {
|
||||
t.Fatalf("url_template = %q, want raw %q", urlTemplate, row.URLTemplate)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMapTileSourceHashFunction(t *testing.T) {
|
||||
hash1 := MapTileSourceHash("https://tile.openstreetmap.jp/{z}/{x}/{y}.png")
|
||||
hash2 := MapTileSourceHash("https://tile.openstreetmap.jp/{z}/{x}/{y}.png")
|
||||
hash3 := MapTileSourceHash("https://other.example.com/{z}/{x}/{y}.png")
|
||||
|
||||
if hash1 != hash2 {
|
||||
t.Fatal("hash should be deterministic")
|
||||
}
|
||||
if len(hash1) != 64 {
|
||||
t.Fatalf("hash length = %d, want 64", len(hash1))
|
||||
}
|
||||
if hash1 == hash3 {
|
||||
t.Fatal("different URLs should produce different hashes")
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
package store
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestRuntimeSettingsDefaultAndUpdates(t *testing.T) {
|
||||
st := openTestStore(t)
|
||||
defer st.Close()
|
||||
|
||||
settings, err := st.GetRuntimeSettings()
|
||||
if err != nil {
|
||||
t.Fatalf("GetRuntimeSettings() error = %v", err)
|
||||
}
|
||||
if settings.AllowEncryptedForwarding {
|
||||
t.Fatalf("AllowEncryptedForwarding = true, want false")
|
||||
}
|
||||
|
||||
if _, err := st.SetBoolRuntimeSetting(RuntimeSettingAllowEncryptedForwarding, true, "test setting"); err != nil {
|
||||
t.Fatalf("SetBoolRuntimeSetting(true) error = %v", err)
|
||||
}
|
||||
settings, err = st.GetRuntimeSettings()
|
||||
if err != nil {
|
||||
t.Fatalf("GetRuntimeSettings() after true error = %v", err)
|
||||
}
|
||||
if !settings.AllowEncryptedForwarding {
|
||||
t.Fatalf("AllowEncryptedForwarding = false, want true")
|
||||
}
|
||||
|
||||
if _, err := st.SetBoolRuntimeSetting(RuntimeSettingAllowEncryptedForwarding, false, "test setting"); err != nil {
|
||||
t.Fatalf("SetBoolRuntimeSetting(false) error = %v", err)
|
||||
}
|
||||
settings, err = st.GetRuntimeSettings()
|
||||
if err != nil {
|
||||
t.Fatalf("GetRuntimeSettings() after false error = %v", err)
|
||||
}
|
||||
if settings.AllowEncryptedForwarding {
|
||||
t.Fatalf("AllowEncryptedForwarding = true, want false")
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
// 测试 helper —— 为从 main 包搬过来的测试提供它们原本依赖的小写函数。
|
||||
// 这些 helper 不暴露给生产代码使用;它们的行为应当与 main 包对应实现保持一致。
|
||||
|
||||
// verifyPassword 复刻 auth.go 中的 bcrypt 校验,用于 user_store 的测试。
|
||||
func verifyPassword(hash, password string) bool {
|
||||
return bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) == nil
|
||||
}
|
||||
|
||||
// publicMapTileSourceDTO 复刻 admin_map_source_routes.go 中的同名函数,
|
||||
// 仅供 map_source_store_test.go 验证 ProxyEnabled 时 URL 是否被改写。
|
||||
// 这里返回 map[string]any 而非 gin.H 以避免引入 gin 依赖。
|
||||
func publicMapTileSourceDTO(row MapTileSourceRecord) map[string]any {
|
||||
urlTemplate := row.URLTemplate
|
||||
if row.ProxyEnabled {
|
||||
hash := row.URLTemplateHash
|
||||
if hash == "" {
|
||||
hash = MapTileSourceHash(row.URLTemplate)
|
||||
}
|
||||
urlTemplate = "/api/map/" + hash + "?x={x}&y={y}&z={z}"
|
||||
}
|
||||
return map[string]any{
|
||||
"id": row.ID,
|
||||
"name": row.Name,
|
||||
"url_template": urlTemplate,
|
||||
"attribution": row.Attribution,
|
||||
"max_zoom": row.MaxZoom,
|
||||
"enabled": row.Enabled,
|
||||
"is_default": row.IsDefault,
|
||||
"proxy_enabled": row.ProxyEnabled,
|
||||
}
|
||||
}
|
||||
|
||||
// newDBWriteQueue 是 db_write_queue_test.go 期望的旧名字。重新导出供测试使用。
|
||||
var newDBWriteQueue = NewWriteQueue
|
||||
|
||||
// 让 strings 不会被 import-but-not-used(如果上面用不到,就算了——保留以应对将来扩展)
|
||||
var _ = strings.TrimSpace
|
||||
@@ -1,27 +0,0 @@
|
||||
// Package testutil 提供给其它包测试使用的 store 临时实例工厂。
|
||||
//
|
||||
// 重构前 db_test.go 中的 openTestStore helper 被 8+ 个测试文件复用;
|
||||
// 现在抽到这里,让 store 包外的测试也可以零样板地拿到一个临时 SQLite store。
|
||||
package testutil
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"meshtastic_mqtt_server/internal/config"
|
||||
"meshtastic_mqtt_server/internal/store"
|
||||
)
|
||||
|
||||
// OpenStore 返回一个写在 t.TempDir() 中的临时 SQLite store。
|
||||
// 测试结束时调用方需要 defer st.Close()。
|
||||
func OpenStore(t *testing.T) *store.Store {
|
||||
t.Helper()
|
||||
st, err := store.OpenStore(config.DatabaseConfig{
|
||||
Driver: config.DriverSQLite,
|
||||
SQLite: config.SQLiteConfig{Path: filepath.Join(t.TempDir(), "mesh_mqtt_go.db")},
|
||||
}, false)
|
||||
if err != nil {
|
||||
t.Fatalf("OpenStore() error = %v", err)
|
||||
}
|
||||
return st
|
||||
}
|
||||
Reference in New Issue
Block a user