新增几个数据表

This commit is contained in:
2026-06-03 17:14:49 +08:00
parent f0d6c5a96a
commit 6ccced6cd3
4 changed files with 583 additions and 51 deletions
+194 -4
View File
@@ -11,7 +11,7 @@ func TestOpenStoreCreatesTables(t *testing.T) {
st := openTestStore(t)
defer st.Close()
for _, table := range []string{"nodeinfo_map", "text_message"} {
for _, table := range []string{"nodeinfo_map", "text_message", "position", "telemetry", "routing", "traceroute"} {
var name string
if err := rawTestDB(t, st).QueryRow("SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?", table).Scan(&name); err != nil {
t.Fatalf("%s table missing: %v", table, err)
@@ -283,6 +283,139 @@ func TestInsertTextMessageRequiresFields(t *testing.T) {
}
}
func TestInsertPositionAppendsRows(t *testing.T) {
st := openTestStore(t)
defer st.Close()
clientInfo := mqttClientInfo{ClientID: "client-1", RemoteAddr: "127.0.0.1:54321", RemoteHost: "127.0.0.1", RemotePort: "54321"}
if err := st.InsertPosition(positionTestRecord(), clientInfo); err != nil {
t.Fatalf("first InsertPosition() error = %v", err)
}
if err := st.InsertPosition(positionTestRecord(), clientInfo); err != nil {
t.Fatalf("second InsertPosition() error = %v", err)
}
var count int
if err := rawTestDB(t, st).QueryRow("SELECT COUNT(*) FROM position WHERE from_id = ?", "!12345678").Scan(&count); err != nil {
t.Fatal(err)
}
if count != 2 {
t.Fatalf("position count = %d, want 2", count)
}
var latitude, longitude float64
var altitude int64
var locationSource, remoteHost string
if err := rawTestDB(t, st).QueryRow("SELECT latitude, longitude, altitude, location_source, mqtt_remote_host FROM position ORDER BY id LIMIT 1").Scan(&latitude, &longitude, &altitude, &locationSource, &remoteHost); err != nil {
t.Fatal(err)
}
if latitude != 42.5 || longitude != -83.1 || altitude != 200 || locationSource != "LOC_INTERNAL" || remoteHost != "127.0.0.1" {
t.Fatalf("position row = lat %v lon %v alt %v source %q remote %q", latitude, longitude, altitude, locationSource, remoteHost)
}
}
func TestInsertTelemetryAppendsRowsAndStoresMetricsJSON(t *testing.T) {
st := openTestStore(t)
defer st.Close()
if err := st.InsertTelemetry(telemetryTestRecord(), mqttClientInfo{}); err != nil {
t.Fatalf("InsertTelemetry() error = %v", err)
}
var telemetryType, metricsJSON, contentJSON string
if err := rawTestDB(t, st).QueryRow("SELECT telemetry_type, metrics_json, content_json FROM telemetry LIMIT 1").Scan(&telemetryType, &metricsJSON, &contentJSON); err != nil {
t.Fatal(err)
}
if telemetryType != "device_metrics" {
t.Fatalf("telemetry_type = %q, want device_metrics", telemetryType)
}
if !strings.Contains(metricsJSON, "battery_level") || !strings.Contains(metricsJSON, "voltage") {
t.Fatalf("metrics_json = %q, want battery_level and voltage", metricsJSON)
}
if !strings.Contains(contentJSON, "telemetry") {
t.Fatalf("content_json = %q, want telemetry", contentJSON)
}
}
func TestInsertRoutingAndTracerouteAppendRows(t *testing.T) {
st := openTestStore(t)
defer st.Close()
if err := st.InsertRouting(routingTestRecord(), mqttClientInfo{}); err != nil {
t.Fatalf("first InsertRouting() error = %v", err)
}
if err := st.InsertRouting(routingTestRecord(), mqttClientInfo{}); err != nil {
t.Fatalf("second InsertRouting() error = %v", err)
}
if err := st.InsertTraceroute(tracerouteTestRecord(), mqttClientInfo{}); err != nil {
t.Fatalf("first InsertTraceroute() error = %v", err)
}
if err := st.InsertTraceroute(tracerouteTestRecord(), mqttClientInfo{}); err != nil {
t.Fatalf("second InsertTraceroute() error = %v", err)
}
for _, table := range []string{"routing", "traceroute"} {
var count int
if err := rawTestDB(t, st).QueryRow("SELECT COUNT(*) FROM "+table+" WHERE from_id = ?", "!12345678").Scan(&count); err != nil {
t.Fatal(err)
}
if count != 2 {
t.Fatalf("%s count = %d, want 2", table, count)
}
var packetID int64
var contentJSON string
if err := rawTestDB(t, st).QueryRow("SELECT packet_id, content_json FROM "+table+" ORDER BY id LIMIT 1").Scan(&packetID, &contentJSON); err != nil {
t.Fatal(err)
}
if packetID != 42 || !strings.Contains(contentJSON, table) {
t.Fatalf("%s row packet_id=%d content_json=%q", table, packetID, contentJSON)
}
}
}
func TestInsertPacketTablesRequireFields(t *testing.T) {
st := openTestStore(t)
defer st.Close()
tests := []struct {
name string
insert func(map[string]any) error
record map[string]any
}{
{name: "position", insert: func(r map[string]any) error { return st.InsertPosition(r, mqttClientInfo{}) }, record: positionTestRecord()},
{name: "telemetry", insert: func(r map[string]any) error { return st.InsertTelemetry(r, mqttClientInfo{}) }, record: telemetryTestRecord()},
{name: "routing", insert: func(r map[string]any) error { return st.InsertRouting(r, mqttClientInfo{}) }, record: routingTestRecord()},
{name: "traceroute", insert: func(r map[string]any) error { return st.InsertTraceroute(r, mqttClientInfo{}) }, record: tracerouteTestRecord()},
}
for _, tt := range tests {
wrongType := cloneRecord(tt.record)
wrongType["type"] = "text_message"
if err := tt.insert(wrongType); err == nil || !strings.Contains(err.Error(), tt.name) {
t.Fatalf("%s wrong type error = %v, want %s", tt.name, err, tt.name)
}
missingFrom := cloneRecord(tt.record)
delete(missingFrom, "from")
if err := tt.insert(missingFrom); err == nil || !strings.Contains(err.Error(), "from") {
t.Fatalf("%s missing from error = %v, want from error", tt.name, err)
}
missingFromNum := cloneRecord(tt.record)
delete(missingFromNum, "from_num")
if err := tt.insert(missingFromNum); err == nil || !strings.Contains(err.Error(), "from_num") {
t.Fatalf("%s missing from_num error = %v, want from_num error", tt.name, err)
}
missingTopic := cloneRecord(tt.record)
delete(missingTopic, "topic")
if err := tt.insert(missingTopic); err == nil || !strings.Contains(err.Error(), "topic") {
t.Fatalf("%s missing topic error = %v, want topic error", tt.name, err)
}
}
}
func openTestStore(t *testing.T) *store {
t.Helper()
st, err := openStore(databaseConfig{
@@ -341,21 +474,78 @@ func mapReportRecord(longName string) map[string]any {
}
func textMessageTestRecord(text any) map[string]any {
record := commonPacketTestRecord("text_message", "TEXT_MESSAGE_APP")
record["text"] = text
return record
}
func positionTestRecord() map[string]any {
record := commonPacketTestRecord("position", "POSITION_APP")
record["latitude"] = 42.5
record["longitude"] = -83.1
record["altitude"] = int32(200)
record["time"] = uint32(123456)
record["location_source"] = "LOC_INTERNAL"
record["altitude_source"] = "ALT_INTERNAL"
record["timestamp"] = uint32(123456)
record["timestamp_millis_adjust"] = uint32(10)
record["altitude_hae"] = int32(210)
record["altitude_geoidal_separation"] = int32(20)
record["pdop"] = 1.1
record["hdop"] = 1.2
record["vdop"] = 1.3
record["gps_accuracy"] = uint32(1000)
record["ground_speed"] = uint32(2)
record["ground_track"] = 180.5
record["fix_quality"] = uint32(1)
record["fix_type"] = uint32(3)
record["sats_in_view"] = uint32(8)
record["sensor_id"] = uint32(1)
record["next_update"] = uint32(60)
record["seq_number"] = uint32(7)
record["precision_bits"] = uint32(16)
return record
}
func telemetryTestRecord() map[string]any {
record := commonPacketTestRecord("telemetry", "TELEMETRY_APP")
record["time"] = uint32(123456)
record["telemetry_type"] = "device_metrics"
record["metrics"] = map[string]any{"battery_level": 85, "voltage": 4.1}
return record
}
func routingTestRecord() map[string]any {
return commonPacketTestRecord("routing", "ROUTING_APP")
}
func tracerouteTestRecord() map[string]any {
return commonPacketTestRecord("traceroute", "TRACEROUTE_APP")
}
func commonPacketTestRecord(recordType, portnum string) map[string]any {
return map[string]any{
"type": "text_message",
"type": recordType,
"topic": "msh/US/test",
"channel_id": "LongFast",
"gateway_id": "!gateway",
"from": "!12345678",
"from_num": uint32(0x12345678),
"text": text,
"packet_id": uint32(42),
"packet_to": "!ffffffff",
"packet_to_num": uint32(0xffffffff),
"portnum": "TEXT_MESSAGE_APP",
"portnum": portnum,
"payload_len": 5,
"payload_variant": "decoded",
"via_mqtt": true,
"pki_encrypted": false,
}
}
func cloneRecord(record map[string]any) map[string]any {
clone := make(map[string]any, len(record))
for key, value := range record {
clone[key] = value
}
return clone
}