211 lines
5.3 KiB
Go
211 lines
5.3 KiB
Go
package db
|
|
|
|
import (
|
|
"database/sql"
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"crypto-miner-server/internal/models"
|
|
)
|
|
|
|
func insertBuild(t *testing.T, d *Database, b *models.BuildRecord) {
|
|
t.Helper()
|
|
if err := d.InsertBuild(b); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestBuildCRUDAndList(t *testing.T) {
|
|
d := openTestDB(t)
|
|
|
|
created := time.Now().UTC().Truncate(time.Second)
|
|
build := &models.BuildRecord{
|
|
ID: "build-001",
|
|
WorkerName: "rig1",
|
|
ServerURL: "http://localhost:8080",
|
|
Wallet: "48xyz",
|
|
Threads: 4,
|
|
FileSize: 1024,
|
|
BundleSize: 2048,
|
|
FilePath: "/data/builds/build-001.zip",
|
|
FileName: "agent.zip",
|
|
DownloadURL: "/api/v1/builds/build-001/download",
|
|
Platform: "windows",
|
|
CreatedAt: created,
|
|
PoolHost: "pool.example.com",
|
|
PoolPort: 443,
|
|
PoolTLS: true,
|
|
PoolPass: "x",
|
|
}
|
|
insertBuild(t, d, build)
|
|
|
|
got, err := d.GetBuild("build-001")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got.WorkerName != "rig1" || got.Platform != "windows" || got.BundleSize != 2048 {
|
|
t.Fatalf("build mismatch: %+v", got)
|
|
}
|
|
if got.Pinned {
|
|
t.Fatal("new build should not be pinned")
|
|
}
|
|
|
|
list, err := d.ListBuilds(10)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(list) != 1 || list[0].ID != "build-001" {
|
|
t.Fatalf("list builds: %+v", list)
|
|
}
|
|
}
|
|
|
|
func TestGetBuildNotFound(t *testing.T) {
|
|
d := openTestDB(t)
|
|
_, err := d.GetBuild("missing")
|
|
if !errors.Is(err, sql.ErrNoRows) {
|
|
t.Fatalf("expected sql.ErrNoRows, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestSetPinnedBuild(t *testing.T) {
|
|
d := openTestDB(t)
|
|
now := time.Now()
|
|
insertBuild(t, d, &models.BuildRecord{ID: "b1", WorkerName: "w", ServerURL: "u", Wallet: "w", CreatedAt: now})
|
|
insertBuild(t, d, &models.BuildRecord{ID: "b2", WorkerName: "w", ServerURL: "u", Wallet: "w", CreatedAt: now.Add(time.Second)})
|
|
|
|
if err := d.SetPinnedBuild("b1"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
b1, _ := d.GetBuild("b1")
|
|
b2, _ := d.GetBuild("b2")
|
|
if !b1.Pinned || b2.Pinned {
|
|
t.Fatalf("pin state wrong: b1=%v b2=%v", b1.Pinned, b2.Pinned)
|
|
}
|
|
|
|
if err := d.SetPinnedBuild(""); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
b1, _ = d.GetBuild("b1")
|
|
if b1.Pinned {
|
|
t.Fatal("empty id should unpin all builds")
|
|
}
|
|
}
|
|
|
|
func TestSetPinnedBuildUnknownID(t *testing.T) {
|
|
d := openTestDB(t)
|
|
now := time.Now()
|
|
insertBuild(t, d, &models.BuildRecord{ID: "b1", WorkerName: "w", ServerURL: "u", Wallet: "w", CreatedAt: now, Pinned: true})
|
|
|
|
err := d.SetPinnedBuild("missing")
|
|
if err == nil {
|
|
t.Fatal("expected error for unknown build id")
|
|
}
|
|
if !strings.Contains(err.Error(), "not found") {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
b1, _ := d.GetBuild("b1")
|
|
if b1.Pinned {
|
|
t.Fatal("unknown id should leave builds unpinned, not keep prior pin")
|
|
}
|
|
}
|
|
|
|
func TestGetLatestBuildForPlatform(t *testing.T) {
|
|
d := openTestDB(t)
|
|
base := time.Now().UTC().Truncate(time.Second)
|
|
|
|
insertBuild(t, d, &models.BuildRecord{
|
|
ID: "old-linux", WorkerName: "w", ServerURL: "u", Wallet: "w",
|
|
Platform: "linux", CreatedAt: base,
|
|
})
|
|
insertBuild(t, d, &models.BuildRecord{
|
|
ID: "new-linux", WorkerName: "w", ServerURL: "u", Wallet: "w",
|
|
Platform: "linux", CreatedAt: base.Add(time.Minute),
|
|
})
|
|
insertBuild(t, d, &models.BuildRecord{
|
|
ID: "new-windows", WorkerName: "w", ServerURL: "u", Wallet: "w",
|
|
Platform: "windows", CreatedAt: base.Add(2 * time.Minute),
|
|
})
|
|
|
|
latestLinux, err := d.GetLatestBuildForPlatform("linux")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if latestLinux.ID != "new-linux" {
|
|
t.Fatalf("latest linux: got %q", latestLinux.ID)
|
|
}
|
|
|
|
if err := d.SetPinnedBuild("old-linux"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
pinnedLinux, err := d.GetLatestBuildForPlatform("linux")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if pinnedLinux.ID != "old-linux" {
|
|
t.Fatalf("pinned linux: got %q", pinnedLinux.ID)
|
|
}
|
|
|
|
anyLatest, err := d.GetLatestBuildForPlatform("any")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if anyLatest.ID != "old-linux" {
|
|
t.Fatalf("any with pinned: got %q want old-linux", anyLatest.ID)
|
|
}
|
|
|
|
if err := d.SetPinnedBuild(""); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
emptyPlatform, err := d.GetLatestBuildForPlatform("")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if emptyPlatform.ID != "new-windows" {
|
|
t.Fatalf("empty platform latest: got %q", emptyPlatform.ID)
|
|
}
|
|
}
|
|
|
|
func TestGetLatestBuildForPlatformEmpty(t *testing.T) {
|
|
d := openTestDB(t)
|
|
_, err := d.GetLatestBuildForPlatform("linux")
|
|
if !errors.Is(err, sql.ErrNoRows) {
|
|
t.Fatalf("expected sql.ErrNoRows on empty db, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestListBuildsOlderThanAndDeleteBuild(t *testing.T) {
|
|
d := openTestDB(t)
|
|
cutoff := time.Now().UTC().Truncate(time.Second)
|
|
oldTime := cutoff.Add(-time.Hour)
|
|
newTime := cutoff.Add(time.Hour)
|
|
|
|
insertBuild(t, d, &models.BuildRecord{
|
|
ID: "old-build", WorkerName: "w", ServerURL: "u", Wallet: "w", CreatedAt: oldTime,
|
|
})
|
|
insertBuild(t, d, &models.BuildRecord{
|
|
ID: "new-build", WorkerName: "w", ServerURL: "u", Wallet: "w", CreatedAt: newTime,
|
|
})
|
|
|
|
older, err := d.ListBuildsOlderThan(cutoff)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(older) != 1 || older[0].ID != "old-build" {
|
|
t.Fatalf("ListBuildsOlderThan: %+v", older)
|
|
}
|
|
|
|
if err := d.DeleteBuild("old-build"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
_, err = d.GetBuild("old-build")
|
|
if !errors.Is(err, sql.ErrNoRows) {
|
|
t.Fatalf("expected deleted build missing, got %v", err)
|
|
}
|
|
|
|
if err := d.DeleteBuild("never-existed"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|