Netpulse_SasS/server/internal/gitstore/store_test.go
byrsapty 65b37a28ff
Some checks are pending
CI / web (push) Waiting to run
CI / server (push) Waiting to run
CI / agent (push) Waiting to run
Git-двигун NCM: версіювання конфігів, переливання історії, довільне порівняння
go-git, чистий Go без cgo. Голий репозиторій на тенанта, гілка на
пристрій за ідентифікатором (ім'я змінюють, історія не має від цього
розсипатись), файл за іменем. Однаковий вміст нового коміту не створює.

netpulse-gitsync переливає накопичену історію й відтворює втрачений
репозиторій із бази — тіла конфігів там і так лежать зашифрованими.

У вебі з'явився вибір версії, з якою порівнювати: сервер це вмів
(?from=), інтерфейс — ні.

Дорогою виправлено чотири тести, які CI запустив уперше з базою.
Серед них справжня помилка: машинний токен не міг читати мапи —
ACL мап отримував порожній рядок замість uuid і давав 500.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 13:15:14 +03:00

140 lines
4.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package gitstore
import (
"os/exec"
"path/filepath"
"strings"
"testing"
"time"
)
func write(t *testing.T, s *Store, branch, path, body string) Result {
t.Helper()
r, err := s.Write(Commit{
Repo: "t.git",
Branch: branch,
Path: path,
Body: []byte(body),
Message: "тест",
Author: "QA",
Email: "qa@netpulse.local",
When: time.Date(2026, 8, 25, 12, 0, 0, 0, time.UTC),
})
if err != nil {
t.Fatalf("запис: %v", err)
}
return r
}
func TestWriteReadHistory(t *testing.T) {
s := New(t.TempDir())
first := write(t, s, "device/d1", "sw-01/running.cfg", "hostname sw-01\n")
if first.Unchanged {
t.Fatal("перший запис не може бути незміненим")
}
if len(first.CommitSHA) != 40 {
t.Fatalf("commit sha не схожий на git: %q", first.CommitSHA)
}
// Той самий вміст не створює коміту — інакше історія заростає
// порожніми записами від нічних бекапів.
same := write(t, s, "device/d1", "sw-01/running.cfg", "hostname sw-01\n")
if !same.Unchanged {
t.Fatal("повторний запис того самого вмісту дав новий коміт")
}
if same.CommitSHA != first.CommitSHA {
t.Fatalf("незмінений запис змінив коміт: %s → %s", first.CommitSHA, same.CommitSHA)
}
second := write(t, s, "device/d1", "sw-01/running.cfg", "hostname sw-01\nntp server 1.1.1.1\n")
if second.CommitSHA == first.CommitSHA {
t.Fatal("зміна вмісту не дала нового коміту")
}
body, err := s.Read("t.git", first.CommitSHA, "sw-01/running.cfg")
if err != nil {
t.Fatalf("читання першої версії: %v", err)
}
if string(body) != "hostname sw-01\n" {
t.Fatalf("перша версія зіпсована: %q", body)
}
hist, err := s.History("t.git", "device/d1", 0)
if err != nil {
t.Fatalf("історія: %v", err)
}
if len(hist) != 2 {
t.Fatalf("очікував 2 коміти, маю %d", len(hist))
}
if hist[0].CommitSHA != second.CommitSHA {
t.Fatal("історія не починається з найновішого")
}
if hist[0].Author != "QA" {
t.Fatalf("автор загубився: %q", hist[0].Author)
}
}
// Гілки різних пристроїв не мають бачити одна одну: інакше історія
// одного комутатора виявиться перемішаною з чужою.
func TestBranchesIndependent(t *testing.T) {
s := New(t.TempDir())
write(t, s, "device/d1", "sw-01/running.cfg", "a\n")
write(t, s, "device/d2", "sw-02/running.cfg", "b\n")
write(t, s, "device/d1", "sw-01/running.cfg", "a2\n")
h1, _ := s.History("t.git", "device/d1", 0)
h2, _ := s.History("t.git", "device/d2", 0)
if len(h1) != 2 || len(h2) != 1 {
t.Fatalf("гілки перемішались: d1=%d d2=%d", len(h1), len(h2))
}
}
// Кілька зрізів одного пристрою живуть в одній гілці різними файлами.
func TestSeveralConfigTypes(t *testing.T) {
s := New(t.TempDir())
write(t, s, "device/d1", "sw-01/running.cfg", "running\n")
last := write(t, s, "device/d1", "sw-01/startup.cfg", "startup\n")
running, err := s.Read("t.git", last.CommitSHA, "sw-01/running.cfg")
if err != nil {
t.Fatalf("running після додавання startup: %v", err)
}
if string(running) != "running\n" {
t.Fatalf("сусідній файл затерто: %q", running)
}
}
// Дерева, зібрані вручну, мусять бути коректними з погляду самого git.
// Це єдина перевірка, яка ловить неправильний порядок записів у дереві:
// go-git такий об'єкт приймає й читає, а git — ні.
func TestFsckClean(t *testing.T) {
if _, err := exec.LookPath("git"); err != nil {
t.Skip("git не знайдено")
}
dir := t.TempDir()
s := New(dir)
write(t, s, "device/d1", "sw-01/running.cfg", "a\n")
write(t, s, "device/d1", "sw-01/startup.cfg", "b\n")
write(t, s, "device/d1", "kyiv/dc1/sw-01/vlan.cfg", "c\n")
write(t, s, "device/d1", "sw-01/running.cfg", "a2\n")
out, err := exec.Command("git", "--git-dir", filepath.Join(dir, "t.git"),
"fsck", "--strict").CombinedOutput()
if err != nil {
t.Fatalf("git fsck: %v\n%s", err, out)
}
if s := strings.TrimSpace(string(out)); s != "" {
t.Fatalf("git fsck поскаржився:\n%s", s)
}
}
func TestDisabled(t *testing.T) {
s := New("")
if _, err := s.Write(Commit{Repo: "t.git", Branch: "b", Path: "p", Body: []byte("x")}); err == nil {
t.Fatal("порожній корінь мав дати помилку")
}
}