Некоторые проверки не удались
CI / test (push) Successful in 2m5s
Docker / Build and publish worker image (push) Failing after 31s
223 строки
6.6 KiB
Go
223 строки
6.6 KiB
Go
package models_test
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"gorm.io/gorm"
|
|
|
|
"rocketgit.ru/rsmon/worker/app/models"
|
|
)
|
|
|
|
func TestServer_RotateToken(t *testing.T) {
|
|
models.Drop()
|
|
models.Migrate()
|
|
|
|
acc := &models.Account{Name: "rotate-token-account"}
|
|
require.NoError(t, models.DB().Create(acc).Error)
|
|
|
|
srv := &models.Server{
|
|
AccountID: acc.ID,
|
|
Name: "rotate-target",
|
|
Slug: "rotate-target",
|
|
Region: "local",
|
|
}
|
|
require.NoError(t, models.DB().Create(srv).Error)
|
|
|
|
t1 := models.GenerateServerToken()
|
|
require.NoError(t, models.RotateServerToken(nil, srv.ID, t1))
|
|
|
|
got, err := models.FindServerByToken(t1)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, srv.ID, got.ID)
|
|
|
|
t2 := models.GenerateServerToken()
|
|
require.NoError(t, models.RotateServerToken(nil, srv.ID, t2))
|
|
|
|
// Old token no longer matches.
|
|
_, err = models.FindServerByToken(t1)
|
|
assert.ErrorIs(t, err, gorm.ErrRecordNotFound)
|
|
|
|
got2, err := models.FindServerByToken(t2)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, srv.ID, got2.ID)
|
|
|
|
// Generated tokens are hex-encoded 32 bytes (64 chars).
|
|
assert.Len(t, t1, 64)
|
|
assert.NotEqual(t, t1, t2)
|
|
}
|
|
|
|
func TestServer_InventoryFields_Default(t *testing.T) {
|
|
models.Drop()
|
|
models.Migrate()
|
|
|
|
acc := &models.Account{Name: "inv-defaults"}
|
|
require.NoError(t, models.DB().Create(acc).Error)
|
|
|
|
srv := &models.Server{
|
|
AccountID: acc.ID,
|
|
Name: "fresh-server",
|
|
Slug: "fresh-server",
|
|
Region: "local",
|
|
}
|
|
require.NoError(t, models.DB().Create(srv).Error)
|
|
|
|
got := models.Server{}
|
|
require.NoError(t, models.DB().First(&got, srv.ID).Error)
|
|
assert.Equal(t, models.ServerKindProduction, got.Kind)
|
|
assert.Equal(t, 0, got.PriceCents)
|
|
assert.False(t, got.Paused)
|
|
}
|
|
|
|
func TestServer_KindEnum_OnlyAllowsValidValues(t *testing.T) {
|
|
models.Drop()
|
|
models.Migrate()
|
|
|
|
acc := &models.Account{Name: "inv-enum"}
|
|
require.NoError(t, models.DB().Create(acc).Error)
|
|
|
|
srv := &models.Server{
|
|
AccountID: acc.ID,
|
|
Name: "kinder",
|
|
Slug: "kinder",
|
|
Region: "local",
|
|
Kind: models.ServerKindStaging,
|
|
}
|
|
require.NoError(t, models.DB().Create(srv).Error)
|
|
|
|
got := models.Server{}
|
|
require.NoError(t, models.DB().First(&got, srv.ID).Error)
|
|
assert.Equal(t, models.ServerKindStaging, got.Kind)
|
|
|
|
// Inserting an invalid value via raw SQL fails the enum check.
|
|
err := models.DB().Exec(
|
|
"INSERT INTO servers (account_id, name, slug, region, kind) VALUES (?, ?, ?, ?, ?)",
|
|
acc.ID, "bad-kinder", "bad-kinder", "local", "scrapped",
|
|
).Error
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "invalid input value for enum")
|
|
}
|
|
|
|
func TestSite_Slugify(t *testing.T) {
|
|
cases := []struct {
|
|
in, want string
|
|
}{
|
|
{"Cafe", "cafe"}, // cyrillic stripped (latin-only rule, see Slugify)
|
|
{" Spaces Everywhere ", "spaces-everywhere"},
|
|
{"dots.and-dashes_and spaces", "dots-and-dashes-and-spaces"},
|
|
{"", "site"},
|
|
{"-leading-and-trailing-", "leading-and-trailing"},
|
|
{"mix_of.dots-dashes spaces", "mix-of-dots-dashes-spaces"},
|
|
}
|
|
for _, c := range cases {
|
|
t.Run(c.in, func(t *testing.T) {
|
|
assert.Equal(t, c.want, models.SiteSlugify(c.in))
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSite_FindOrCreateBySlug(t *testing.T) {
|
|
models.Drop()
|
|
models.Migrate()
|
|
|
|
acc := &models.Account{Name: "site-foc"}
|
|
require.NoError(t, models.DB().Create(acc).Error)
|
|
|
|
got, err := models.FindOrCreateSiteBySlug(nil, acc.ID, "my-site")
|
|
require.NoError(t, err)
|
|
require.NotZero(t, got.ID, "row should be persisted")
|
|
assert.Equal(t, "my-site", got.Slug)
|
|
assert.Equal(t, "production", got.Kind)
|
|
assert.True(t, got.IsActive)
|
|
|
|
// Second call returns the same row (idempotent).
|
|
got2, err := models.FindOrCreateSiteBySlug(nil, acc.ID, "my-site")
|
|
require.NoError(t, err)
|
|
assert.Equal(t, got.ID, got2.ID)
|
|
}
|
|
|
|
func TestDeployment_UpsertNginx_MatchesByConfigPath(t *testing.T) {
|
|
models.Drop()
|
|
models.Migrate()
|
|
|
|
acc := &models.Account{Name: "dep-upsert"}
|
|
require.NoError(t, models.DB().Create(acc).Error)
|
|
srv := &models.Server{
|
|
AccountID: acc.ID, Name: "host1", Slug: "host1", Region: "local",
|
|
}
|
|
require.NoError(t, models.DB().Create(srv).Error)
|
|
|
|
d1, err := models.UpsertNginxDeployment(nil, acc.ID, srv.ID, "/etc/nginx/sites-enabled/a.conf")
|
|
require.NoError(t, err)
|
|
require.NoError(t, models.DB().Save(d1).Error)
|
|
|
|
// Second call with the same config_path returns the existing row.
|
|
d2, err := models.UpsertNginxDeployment(nil, acc.ID, srv.ID, "/etc/nginx/sites-enabled/a.conf")
|
|
require.NoError(t, err)
|
|
assert.Equal(t, d1.ID, d2.ID, "should reuse the same row on identical config_path")
|
|
|
|
// Different config_path creates a new row.
|
|
d3, err := models.UpsertNginxDeployment(nil, acc.ID, srv.ID, "/etc/nginx/sites-enabled/b.conf")
|
|
require.NoError(t, err)
|
|
assert.NotEqual(t, d1.ID, d3.ID)
|
|
}
|
|
|
|
func TestDeployment_ReconcileMissing_FlipsAction(t *testing.T) {
|
|
models.Drop()
|
|
models.Migrate()
|
|
|
|
acc := &models.Account{Name: "reconcile"}
|
|
require.NoError(t, models.DB().Create(acc).Error)
|
|
srv := &models.Server{
|
|
AccountID: acc.ID, Name: "rec", Slug: "rec", Region: "local",
|
|
}
|
|
require.NoError(t, models.DB().Create(srv).Error)
|
|
|
|
old := time.Now().Add(-2 * time.Hour)
|
|
fresh := models.Deployment{
|
|
AccountID: acc.ID,
|
|
ServerID: &srv.ID,
|
|
Kind: models.DeploymentKindProduction,
|
|
Mode: models.DeploymentModeDedicated,
|
|
Action: models.DeploymentActionOk,
|
|
ConfigPath: ptr("/etc/nginx/old.conf"),
|
|
LastSeenAt: &old,
|
|
}
|
|
require.NoError(t, models.DB().Create(&fresh).Error)
|
|
|
|
recent := models.Deployment{
|
|
AccountID: acc.ID,
|
|
ServerID: &srv.ID,
|
|
Kind: models.DeploymentKindProduction,
|
|
Mode: models.DeploymentModeDedicated,
|
|
Action: models.DeploymentActionOk,
|
|
ConfigPath: ptr("/etc/nginx/recent.conf"),
|
|
LastSeenAt: ptrTime(time.Now()),
|
|
}
|
|
require.NoError(t, models.DB().Create(&recent).Error)
|
|
|
|
cutoff := time.Now().Add(-90 * time.Second)
|
|
marked, err := models.ReconcileMissingDeployments(srv.ID, models.DeploymentModeDedicated, cutoff)
|
|
require.NoError(t, err)
|
|
assert.EqualValues(t, 1, marked, "only the old row should flip")
|
|
|
|
var oldAfter models.Deployment
|
|
require.NoError(t, models.DB().First(&oldAfter, fresh.ID).Error)
|
|
assert.Equal(t, models.DeploymentActionMissing, oldAfter.Action)
|
|
|
|
var recentAfter models.Deployment
|
|
require.NoError(t, models.DB().First(&recentAfter, recent.ID).Error)
|
|
assert.Equal(t, models.DeploymentActionOk, recentAfter.Action, "fresh row stays ok")
|
|
|
|
// Re-running with the same cutoff is a no-op.
|
|
marked2, err := models.ReconcileMissingDeployments(srv.ID, models.DeploymentModeDedicated, cutoff)
|
|
require.NoError(t, err)
|
|
assert.EqualValues(t, 0, marked2)
|
|
}
|
|
|
|
func ptr(s string) *string { return &s }
|
|
|
|
func ptrTime(t time.Time) *time.Time { return &t }
|