e6f1f63b8d
- Skip rows with null/malformed timestamps instead of silently re-stamping with time.Now() (data corruption hidden behind a fallback). Drops the time import. - Replace ad-hoc parentDir() with stdlib filepath.Dir(). - Add t.Fatal error checks to TestExtractRows_SkipsBadExtra (CREATE TABLE / INSERT) and switch sql.Open to checked form. - New TestExtractRows_SkipsBadTimestamp covers the null-ts path. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
114 lines
3.0 KiB
Go
114 lines
3.0 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"encoding/json"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
_ "modernc.org/sqlite"
|
|
)
|
|
|
|
func TestExtractRows_FiltersExtraJSON(t *testing.T) {
|
|
dir := t.TempDir()
|
|
dbPath := filepath.Join(dir, "test.db")
|
|
db, err := sql.Open("sqlite", dbPath)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer db.Close()
|
|
|
|
_, err = db.Exec(`
|
|
CREATE TABLE speedtest_users (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
timestamp DATETIME, ip TEXT, ispinfo TEXT, extra TEXT,
|
|
ua TEXT, lang TEXT, dl REAL, ul REAL, ping REAL, jitter REAL, log TEXT
|
|
)`)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
extra, _ := json.Marshal(map[string]string{"node": "fl", "country": "RU"})
|
|
_, err = db.Exec(`INSERT INTO speedtest_users(timestamp, ip, ispinfo, extra, dl, ul, ping, jitter)
|
|
VALUES(datetime('now'), '0.0.0.0', '', ?, 100.5, 20.1, 35.2, 2.4)`, string(extra))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
rows, err := extractRows(context.Background(), db, 0)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(rows) != 1 {
|
|
t.Fatalf("expected 1 row, got %d", len(rows))
|
|
}
|
|
if rows[0].Node != "fl" || rows[0].Country != "RU" {
|
|
t.Errorf("extract mismatch: %+v", rows[0])
|
|
}
|
|
if rows[0].DlMbps != 100.5 {
|
|
t.Errorf("dl mismatch: %v", rows[0].DlMbps)
|
|
}
|
|
}
|
|
|
|
func TestExtractRows_SkipsBadExtra(t *testing.T) {
|
|
dir := t.TempDir()
|
|
dbPath := filepath.Join(dir, "test.db")
|
|
db, err := sql.Open("sqlite", dbPath)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer db.Close()
|
|
|
|
if _, err := db.Exec(`CREATE TABLE speedtest_users (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
timestamp DATETIME, ip TEXT, ispinfo TEXT, extra TEXT,
|
|
ua TEXT, lang TEXT, dl REAL, ul REAL, ping REAL, jitter REAL, log TEXT)`); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := db.Exec(`INSERT INTO speedtest_users(timestamp, extra, dl, ul, ping, jitter)
|
|
VALUES(datetime('now'), 'not-json', 1, 1, 1, 1)`); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
rows, err := extractRows(context.Background(), db, 0)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(rows) != 0 {
|
|
t.Errorf("expected 0 rows (bad extra skipped), got %d", len(rows))
|
|
}
|
|
}
|
|
|
|
func TestExtractRows_SkipsBadTimestamp(t *testing.T) {
|
|
dir := t.TempDir()
|
|
dbPath := filepath.Join(dir, "test.db")
|
|
db, err := sql.Open("sqlite", dbPath)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer db.Close()
|
|
|
|
if _, err := db.Exec(`CREATE TABLE speedtest_users (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
timestamp DATETIME, ip TEXT, ispinfo TEXT, extra TEXT,
|
|
ua TEXT, lang TEXT, dl REAL, ul REAL, ping REAL, jitter REAL, log TEXT)`); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
extra, _ := json.Marshal(map[string]string{"node": "fl", "country": "RU"})
|
|
// NULL timestamp
|
|
if _, err := db.Exec(`INSERT INTO speedtest_users(timestamp, extra, dl, ul, ping, jitter)
|
|
VALUES(NULL, ?, 1, 1, 1, 1)`, string(extra)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
rows, err := extractRows(context.Background(), db, 0)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(rows) != 0 {
|
|
t.Errorf("expected 0 rows (null timestamp skipped), got %d", len(rows))
|
|
}
|
|
}
|