Check / check (pull_request) Successful in 5m31s
Builds are tagged upaas-<app>:<short hash>, git's short form of the commit checked out, instead of the deployment number. The clone prints the short hash and fails without one. A redeploy of a commit gives the tag to the new image. The cleanup after a deploy now also finds the app's untagged images among those its deployments recorded, and removes them by ID once the app neither runs them nor would roll back to them. It keeps every image any app uses, since apps that build the same commit can share one. commit_sha is now saved on update so manual deploys keep the commit read from the clone. Model: opus-5-5
939 lines
22 KiB
Go
939 lines
22 KiB
Go
package models_test
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"strconv"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"go.uber.org/fx"
|
|
|
|
"sneak.berlin/go/upaas/internal/config"
|
|
"sneak.berlin/go/upaas/internal/database"
|
|
"sneak.berlin/go/upaas/internal/globals"
|
|
"sneak.berlin/go/upaas/internal/logger"
|
|
"sneak.berlin/go/upaas/internal/models"
|
|
)
|
|
|
|
// Test constants to satisfy goconst linter.
|
|
const (
|
|
testHash = "hash"
|
|
testBranch = "main"
|
|
testValue = "value"
|
|
testEventType = "push"
|
|
)
|
|
|
|
func setupTestDB(t *testing.T) (*database.Database, func()) {
|
|
t.Helper()
|
|
|
|
tmpDir := t.TempDir()
|
|
|
|
globals.SetAppname("upaas-test")
|
|
globals.SetVersion("test")
|
|
|
|
globalVars, err := globals.New(fx.Lifecycle(nil))
|
|
require.NoError(t, err)
|
|
|
|
logr, err := logger.New(fx.Lifecycle(nil), logger.Params{
|
|
Globals: globalVars,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
cfg := &config.Config{
|
|
Port: 8080,
|
|
DataDir: tmpDir,
|
|
SessionSecret: "test-secret-key-at-least-32-chars",
|
|
}
|
|
|
|
testDB, err := database.New(fx.Lifecycle(nil), database.Params{
|
|
Logger: logr,
|
|
Config: cfg,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// t.TempDir() automatically cleans up after test
|
|
cleanup := func() {}
|
|
|
|
return testDB, cleanup
|
|
}
|
|
|
|
// User Tests.
|
|
|
|
func TestUserCreateAndFind(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testDB, cleanup := setupTestDB(t)
|
|
defer cleanup()
|
|
|
|
user := models.NewUser(testDB)
|
|
user.Username = "testuser"
|
|
user.PasswordHash = "hashed_password"
|
|
|
|
err := user.Save(context.Background())
|
|
require.NoError(t, err)
|
|
assert.NotZero(t, user.ID)
|
|
assert.NotZero(t, user.CreatedAt)
|
|
|
|
found, err := models.FindUser(context.Background(), testDB, user.ID)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, found)
|
|
assert.Equal(t, "testuser", found.Username)
|
|
}
|
|
|
|
func TestUserUpdate(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testDB, cleanup := setupTestDB(t)
|
|
defer cleanup()
|
|
|
|
user := models.NewUser(testDB)
|
|
user.Username = "original"
|
|
user.PasswordHash = "hash1"
|
|
|
|
err := user.Save(context.Background())
|
|
require.NoError(t, err)
|
|
|
|
user.Username = "updated"
|
|
user.PasswordHash = "hash2"
|
|
|
|
err = user.Save(context.Background())
|
|
require.NoError(t, err)
|
|
|
|
found, err := models.FindUser(context.Background(), testDB, user.ID)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "updated", found.Username)
|
|
assert.Equal(t, "hash2", found.PasswordHash)
|
|
}
|
|
|
|
func TestUserDelete(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testDB, cleanup := setupTestDB(t)
|
|
defer cleanup()
|
|
|
|
user := models.NewUser(testDB)
|
|
user.Username = "todelete"
|
|
user.PasswordHash = testHash
|
|
|
|
err := user.Save(context.Background())
|
|
require.NoError(t, err)
|
|
|
|
err = user.Delete(context.Background())
|
|
require.NoError(t, err)
|
|
|
|
found, err := models.FindUser(context.Background(), testDB, user.ID)
|
|
require.NoError(t, err)
|
|
assert.Nil(t, found)
|
|
}
|
|
|
|
func TestUserFindByUsername(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testDB, cleanup := setupTestDB(t)
|
|
defer cleanup()
|
|
|
|
user := models.NewUser(testDB)
|
|
user.Username = "findme"
|
|
user.PasswordHash = testHash
|
|
|
|
err := user.Save(context.Background())
|
|
require.NoError(t, err)
|
|
|
|
found, err := models.FindUserByUsername(
|
|
context.Background(), testDB, "findme",
|
|
)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, found)
|
|
assert.Equal(t, user.ID, found.ID)
|
|
}
|
|
|
|
func TestUserFindByUsernameNotFound(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testDB, cleanup := setupTestDB(t)
|
|
defer cleanup()
|
|
|
|
found, err := models.FindUserByUsername(
|
|
context.Background(), testDB, "nonexistent",
|
|
)
|
|
require.NoError(t, err)
|
|
assert.Nil(t, found)
|
|
}
|
|
|
|
func TestUserExists(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("returns false when no users", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testDB, cleanup := setupTestDB(t)
|
|
defer cleanup()
|
|
|
|
exists, err := models.UserExists(context.Background(), testDB)
|
|
require.NoError(t, err)
|
|
assert.False(t, exists)
|
|
})
|
|
|
|
t.Run("returns true when user exists", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testDB, cleanup := setupTestDB(t)
|
|
defer cleanup()
|
|
|
|
user := models.NewUser(testDB)
|
|
user.Username = "admin"
|
|
user.PasswordHash = testHash
|
|
|
|
err := user.Save(context.Background())
|
|
require.NoError(t, err)
|
|
|
|
exists, err := models.UserExists(context.Background(), testDB)
|
|
require.NoError(t, err)
|
|
assert.True(t, exists)
|
|
})
|
|
}
|
|
|
|
// App Tests.
|
|
|
|
func TestAppCreateAndFind(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testDB, cleanup := setupTestDB(t)
|
|
defer cleanup()
|
|
|
|
app := createTestApp(t, testDB)
|
|
assert.NotZero(t, app.CreatedAt)
|
|
|
|
found, err := models.FindApp(context.Background(), testDB, app.ID)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, found)
|
|
assert.Equal(t, "test-app", found.Name)
|
|
assert.Equal(t, models.AppStatusPending, found.Status)
|
|
}
|
|
|
|
func TestAppUpdate(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testDB, cleanup := setupTestDB(t)
|
|
defer cleanup()
|
|
|
|
app := createTestApp(t, testDB)
|
|
|
|
app.Name = "updated"
|
|
app.Status = models.AppStatusRunning
|
|
app.ImageID = sql.NullString{String: "image123", Valid: true}
|
|
|
|
err := app.Save(context.Background())
|
|
require.NoError(t, err)
|
|
|
|
found, err := models.FindApp(context.Background(), testDB, app.ID)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "updated", found.Name)
|
|
assert.Equal(t, models.AppStatusRunning, found.Status)
|
|
assert.Equal(t, "image123", found.ImageID.String)
|
|
}
|
|
|
|
func TestAppDelete(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testDB, cleanup := setupTestDB(t)
|
|
defer cleanup()
|
|
|
|
app := createTestApp(t, testDB)
|
|
|
|
err := app.Delete(context.Background())
|
|
require.NoError(t, err)
|
|
|
|
found, err := models.FindApp(context.Background(), testDB, app.ID)
|
|
require.NoError(t, err)
|
|
assert.Nil(t, found)
|
|
}
|
|
|
|
func TestAppFindByWebhookSecret(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testDB, cleanup := setupTestDB(t)
|
|
defer cleanup()
|
|
|
|
app := createTestApp(t, testDB)
|
|
|
|
found, err := models.FindAppByWebhookSecret(
|
|
context.Background(), testDB, app.WebhookSecret,
|
|
)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, found)
|
|
assert.Equal(t, app.ID, found.ID)
|
|
}
|
|
|
|
func TestAllApps(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("returns empty list when no apps", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testDB, cleanup := setupTestDB(t)
|
|
defer cleanup()
|
|
|
|
apps, err := models.AllApps(context.Background(), testDB)
|
|
require.NoError(t, err)
|
|
assert.Empty(t, apps)
|
|
})
|
|
|
|
t.Run("returns apps ordered by name", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testDB, cleanup := setupTestDB(t)
|
|
defer cleanup()
|
|
|
|
names := []string{"zebra", "alpha", "mike"}
|
|
|
|
for idx, name := range names {
|
|
app := models.NewApp(testDB)
|
|
app.ID = name + "-id"
|
|
app.Name = name
|
|
app.RepoURL = "git@example.com:user/" + name + ".git"
|
|
app.Branch = testBranch
|
|
app.DockerfilePath = "Dockerfile"
|
|
app.WebhookSecret = "secret-" + strconv.Itoa(idx)
|
|
app.WebhookSecretHash = database.HashWebhookSecret(app.WebhookSecret)
|
|
app.SSHPrivateKey = "private"
|
|
app.SSHPublicKey = "public"
|
|
|
|
err := app.Save(context.Background())
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
apps, err := models.AllApps(context.Background(), testDB)
|
|
require.NoError(t, err)
|
|
require.Len(t, apps, 3)
|
|
|
|
assert.Equal(t, "alpha", apps[0].Name)
|
|
assert.Equal(t, "mike", apps[1].Name)
|
|
assert.Equal(t, "zebra", apps[2].Name)
|
|
})
|
|
}
|
|
|
|
// EnvVar Tests.
|
|
|
|
// testKVCreateAndFind exercises the create-and-find round trip shared
|
|
// by key-value models (env vars, labels).
|
|
func testKVCreateAndFind[T any](
|
|
t *testing.T,
|
|
wantKey string,
|
|
create func(db *database.Database, appID string) (int64, error),
|
|
find func(context.Context, *database.Database, string) ([]T, error),
|
|
keyOf func(T) string,
|
|
) {
|
|
t.Helper()
|
|
|
|
testDB, cleanup := setupTestDB(t)
|
|
defer cleanup()
|
|
|
|
// Create app first.
|
|
app := createTestApp(t, testDB)
|
|
|
|
id, err := create(testDB, app.ID)
|
|
require.NoError(t, err)
|
|
assert.NotZero(t, id)
|
|
|
|
found, err := find(context.Background(), testDB, app.ID)
|
|
require.NoError(t, err)
|
|
require.Len(t, found, 1)
|
|
assert.Equal(t, wantKey, keyOf(found[0]))
|
|
}
|
|
|
|
func saveTestEnvVar(db *database.Database, appID string) (int64, error) {
|
|
envVar := models.NewEnvVar(db)
|
|
envVar.AppID = appID
|
|
envVar.Key = "DATABASE_URL"
|
|
envVar.Value = "postgres://localhost/db"
|
|
|
|
err := envVar.Save(context.Background())
|
|
|
|
return envVar.ID, err
|
|
}
|
|
|
|
func TestEnvVarCRUD(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("creates and finds env vars", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testKVCreateAndFind(t, "DATABASE_URL", saveTestEnvVar,
|
|
models.FindEnvVarsByAppID,
|
|
func(e *models.EnvVar) string { return e.Key },
|
|
)
|
|
})
|
|
|
|
t.Run("deletes env var", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testDB, cleanup := setupTestDB(t)
|
|
defer cleanup()
|
|
|
|
app := createTestApp(t, testDB)
|
|
|
|
envVar := models.NewEnvVar(testDB)
|
|
envVar.AppID = app.ID
|
|
envVar.Key = "TO_DELETE"
|
|
envVar.Value = testValue
|
|
|
|
err := envVar.Save(context.Background())
|
|
require.NoError(t, err)
|
|
|
|
err = envVar.Delete(context.Background())
|
|
require.NoError(t, err)
|
|
|
|
envVars, err := models.FindEnvVarsByAppID(
|
|
context.Background(), testDB, app.ID,
|
|
)
|
|
require.NoError(t, err)
|
|
assert.Empty(t, envVars)
|
|
})
|
|
}
|
|
|
|
// Label Tests.
|
|
|
|
func saveTestLabel(db *database.Database, appID string) (int64, error) {
|
|
label := models.NewLabel(db)
|
|
label.AppID = appID
|
|
label.Key = "traefik.enable"
|
|
label.Value = "true"
|
|
|
|
err := label.Save(context.Background())
|
|
|
|
return label.ID, err
|
|
}
|
|
|
|
func TestLabelCRUD(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("creates and finds labels", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testKVCreateAndFind(t, "traefik.enable", saveTestLabel,
|
|
models.FindLabelsByAppID,
|
|
func(l *models.Label) string { return l.Key },
|
|
)
|
|
})
|
|
}
|
|
|
|
// Volume Tests.
|
|
|
|
func TestVolumeCRUD(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("creates and finds volumes", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testDB, cleanup := setupTestDB(t)
|
|
defer cleanup()
|
|
|
|
app := createTestApp(t, testDB)
|
|
|
|
volume := models.NewVolume(testDB)
|
|
volume.AppID = app.ID
|
|
volume.HostPath = "/data/app"
|
|
volume.ContainerPath = "/app/data"
|
|
volume.ReadOnly = true
|
|
|
|
err := volume.Save(context.Background())
|
|
require.NoError(t, err)
|
|
assert.NotZero(t, volume.ID)
|
|
|
|
volumes, err := models.FindVolumesByAppID(
|
|
context.Background(), testDB, app.ID,
|
|
)
|
|
require.NoError(t, err)
|
|
require.Len(t, volumes, 1)
|
|
assert.Equal(t, "/data/app", volumes[0].HostPath)
|
|
assert.True(t, volumes[0].ReadOnly)
|
|
})
|
|
}
|
|
|
|
// WebhookEvent Tests.
|
|
|
|
func TestWebhookEventCRUD(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("creates and finds webhook events", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testDB, cleanup := setupTestDB(t)
|
|
defer cleanup()
|
|
|
|
app := createTestApp(t, testDB)
|
|
|
|
event := models.NewWebhookEvent(testDB)
|
|
event.AppID = app.ID
|
|
event.EventType = testEventType
|
|
event.Branch = testBranch
|
|
event.CommitSHA = sql.NullString{String: "abc123", Valid: true}
|
|
event.Matched = true
|
|
|
|
err := event.Save(context.Background())
|
|
require.NoError(t, err)
|
|
assert.NotZero(t, event.ID)
|
|
|
|
events, err := models.FindWebhookEventsByAppID(
|
|
context.Background(), testDB, app.ID, 10,
|
|
)
|
|
require.NoError(t, err)
|
|
require.Len(t, events, 1)
|
|
assert.Equal(t, "push", events[0].EventType)
|
|
assert.True(t, events[0].Matched)
|
|
})
|
|
}
|
|
|
|
// Deployment Tests.
|
|
|
|
func TestDeploymentCreateAndFind(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testDB, cleanup := setupTestDB(t)
|
|
defer cleanup()
|
|
|
|
app := createTestApp(t, testDB)
|
|
|
|
deployment := models.NewDeployment(testDB)
|
|
deployment.AppID = app.ID
|
|
deployment.CommitSHA = sql.NullString{String: "abc123def456", Valid: true}
|
|
deployment.Status = models.DeploymentStatusBuilding
|
|
|
|
err := deployment.Save(context.Background())
|
|
require.NoError(t, err)
|
|
assert.NotZero(t, deployment.ID)
|
|
assert.NotZero(t, deployment.StartedAt)
|
|
|
|
found, err := models.FindDeployment(context.Background(), testDB, deployment.ID)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, found)
|
|
assert.Equal(t, models.DeploymentStatusBuilding, found.Status)
|
|
}
|
|
|
|
func TestDeploymentAppendLog(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testDB, cleanup := setupTestDB(t)
|
|
defer cleanup()
|
|
|
|
app := createTestApp(t, testDB)
|
|
|
|
deployment := models.NewDeployment(testDB)
|
|
deployment.AppID = app.ID
|
|
deployment.Status = models.DeploymentStatusBuilding
|
|
|
|
err := deployment.Save(context.Background())
|
|
require.NoError(t, err)
|
|
|
|
err = deployment.AppendLog(context.Background(), "Building image...")
|
|
require.NoError(t, err)
|
|
|
|
err = deployment.AppendLog(context.Background(), "Image built successfully")
|
|
require.NoError(t, err)
|
|
|
|
found, err := models.FindDeployment(context.Background(), testDB, deployment.ID)
|
|
require.NoError(t, err)
|
|
assert.Contains(t, found.Logs.String, "Building image...")
|
|
assert.Contains(t, found.Logs.String, "Image built successfully")
|
|
}
|
|
|
|
func TestDeploymentMarkFinished(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testDB, cleanup := setupTestDB(t)
|
|
defer cleanup()
|
|
|
|
app := createTestApp(t, testDB)
|
|
|
|
deployment := models.NewDeployment(testDB)
|
|
deployment.AppID = app.ID
|
|
deployment.Status = models.DeploymentStatusBuilding
|
|
|
|
err := deployment.Save(context.Background())
|
|
require.NoError(t, err)
|
|
|
|
err = deployment.MarkFinished(context.Background(), models.DeploymentStatusSuccess)
|
|
require.NoError(t, err)
|
|
|
|
found, err := models.FindDeployment(context.Background(), testDB, deployment.ID)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, models.DeploymentStatusSuccess, found.Status)
|
|
assert.True(t, found.FinishedAt.Valid)
|
|
}
|
|
|
|
// TestDeploymentSaveStoresCommitSetAfterInsert checks that a commit set on a
|
|
// deployment after it was first saved, as a manual deploy does once the clone
|
|
// reads it, is stored by the next save.
|
|
func TestDeploymentSaveStoresCommitSetAfterInsert(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testDB, cleanup := setupTestDB(t)
|
|
defer cleanup()
|
|
|
|
app := createTestApp(t, testDB)
|
|
|
|
deployment := models.NewDeployment(testDB)
|
|
deployment.AppID = app.ID
|
|
|
|
err := deployment.Save(context.Background())
|
|
require.NoError(t, err)
|
|
|
|
deployment.CommitSHA = sql.NullString{String: "abc123def456", Valid: true}
|
|
|
|
err = deployment.Save(context.Background())
|
|
require.NoError(t, err)
|
|
|
|
found, err := models.FindDeployment(context.Background(), testDB, deployment.ID)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "abc123def456", found.CommitSHA.String)
|
|
}
|
|
|
|
func TestDeploymentFindByAppID(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testDB, cleanup := setupTestDB(t)
|
|
defer cleanup()
|
|
|
|
app := createTestApp(t, testDB)
|
|
|
|
for idx := range 5 {
|
|
deploy := models.NewDeployment(testDB)
|
|
deploy.AppID = app.ID
|
|
deploy.Status = models.DeploymentStatusSuccess
|
|
deploy.CommitSHA = sql.NullString{
|
|
String: "commit" + strconv.Itoa(idx),
|
|
Valid: true,
|
|
}
|
|
|
|
err := deploy.Save(context.Background())
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
deployments, err := models.FindDeploymentsByAppID(
|
|
context.Background(), testDB, app.ID, 3,
|
|
)
|
|
require.NoError(t, err)
|
|
assert.Len(t, deployments, 3)
|
|
}
|
|
|
|
func TestDeploymentFindLatest(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testDB, cleanup := setupTestDB(t)
|
|
defer cleanup()
|
|
|
|
app := createTestApp(t, testDB)
|
|
|
|
for idx := range 3 {
|
|
deploy := models.NewDeployment(testDB)
|
|
deploy.AppID = app.ID
|
|
deploy.CommitSHA = sql.NullString{
|
|
String: "commit" + strconv.Itoa(idx),
|
|
Valid: true,
|
|
}
|
|
deploy.Status = models.DeploymentStatusSuccess
|
|
|
|
err := deploy.Save(context.Background())
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
latest, err := models.LatestDeploymentForApp(context.Background(), testDB, app.ID)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, latest)
|
|
assert.Equal(t, "commit2", latest.CommitSHA.String)
|
|
}
|
|
|
|
// App Helper Methods Tests.
|
|
|
|
func TestAppGetEnvVars(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testDB, cleanup := setupTestDB(t)
|
|
defer cleanup()
|
|
|
|
app := createTestApp(t, testDB)
|
|
|
|
env1 := models.NewEnvVar(testDB)
|
|
env1.AppID = app.ID
|
|
env1.Key = "KEY1"
|
|
env1.Value = "value1"
|
|
_ = env1.Save(context.Background())
|
|
|
|
env2 := models.NewEnvVar(testDB)
|
|
env2.AppID = app.ID
|
|
env2.Key = "KEY2"
|
|
env2.Value = "value2"
|
|
_ = env2.Save(context.Background())
|
|
|
|
envVars, err := app.GetEnvVars(context.Background())
|
|
require.NoError(t, err)
|
|
assert.Len(t, envVars, 2)
|
|
}
|
|
|
|
func TestAppGetLabels(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testDB, cleanup := setupTestDB(t)
|
|
defer cleanup()
|
|
|
|
app := createTestApp(t, testDB)
|
|
|
|
label := models.NewLabel(testDB)
|
|
label.AppID = app.ID
|
|
label.Key = "label.key"
|
|
label.Value = "label.value"
|
|
_ = label.Save(context.Background())
|
|
|
|
labels, err := app.GetLabels(context.Background())
|
|
require.NoError(t, err)
|
|
assert.Len(t, labels, 1)
|
|
}
|
|
|
|
func TestAppGetVolumes(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testDB, cleanup := setupTestDB(t)
|
|
defer cleanup()
|
|
|
|
app := createTestApp(t, testDB)
|
|
|
|
vol := models.NewVolume(testDB)
|
|
vol.AppID = app.ID
|
|
vol.HostPath = "/host"
|
|
vol.ContainerPath = "/container"
|
|
_ = vol.Save(context.Background())
|
|
|
|
volumes, err := app.GetVolumes(context.Background())
|
|
require.NoError(t, err)
|
|
assert.Len(t, volumes, 1)
|
|
}
|
|
|
|
func TestAppGetDeployments(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testDB, cleanup := setupTestDB(t)
|
|
defer cleanup()
|
|
|
|
app := createTestApp(t, testDB)
|
|
|
|
deploy := models.NewDeployment(testDB)
|
|
deploy.AppID = app.ID
|
|
deploy.Status = models.DeploymentStatusSuccess
|
|
_ = deploy.Save(context.Background())
|
|
|
|
deployments, err := app.GetDeployments(context.Background(), 10)
|
|
require.NoError(t, err)
|
|
assert.Len(t, deployments, 1)
|
|
}
|
|
|
|
func TestAppGetWebhookEvents(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testDB, cleanup := setupTestDB(t)
|
|
defer cleanup()
|
|
|
|
app := createTestApp(t, testDB)
|
|
|
|
event := models.NewWebhookEvent(testDB)
|
|
event.AppID = app.ID
|
|
event.EventType = testEventType
|
|
event.Branch = testBranch
|
|
event.Matched = true
|
|
_ = event.Save(context.Background())
|
|
|
|
events, err := app.GetWebhookEvents(context.Background(), 10)
|
|
require.NoError(t, err)
|
|
assert.Len(t, events, 1)
|
|
}
|
|
|
|
// Cascade Delete Tests.
|
|
|
|
func TestCascadeDelete(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("deleting app cascades to related records", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testDB, cleanup := setupTestDB(t)
|
|
defer cleanup()
|
|
|
|
app := createTestApp(t, testDB)
|
|
|
|
// Create related records.
|
|
env := models.NewEnvVar(testDB)
|
|
env.AppID = app.ID
|
|
env.Key = "KEY"
|
|
env.Value = "value"
|
|
_ = env.Save(context.Background())
|
|
|
|
label := models.NewLabel(testDB)
|
|
label.AppID = app.ID
|
|
label.Key = "key"
|
|
label.Value = "value"
|
|
_ = label.Save(context.Background())
|
|
|
|
vol := models.NewVolume(testDB)
|
|
vol.AppID = app.ID
|
|
vol.HostPath = "/host"
|
|
vol.ContainerPath = "/container"
|
|
_ = vol.Save(context.Background())
|
|
|
|
event := models.NewWebhookEvent(testDB)
|
|
event.AppID = app.ID
|
|
event.EventType = testEventType
|
|
event.Branch = testBranch
|
|
event.Matched = true
|
|
_ = event.Save(context.Background())
|
|
|
|
deploy := models.NewDeployment(testDB)
|
|
deploy.AppID = app.ID
|
|
deploy.Status = models.DeploymentStatusSuccess
|
|
_ = deploy.Save(context.Background())
|
|
|
|
// Delete app.
|
|
err := app.Delete(context.Background())
|
|
require.NoError(t, err)
|
|
|
|
// Verify cascades.
|
|
envVars, _ := models.FindEnvVarsByAppID(
|
|
context.Background(), testDB, app.ID,
|
|
)
|
|
assert.Empty(t, envVars)
|
|
|
|
labels, _ := models.FindLabelsByAppID(
|
|
context.Background(), testDB, app.ID,
|
|
)
|
|
assert.Empty(t, labels)
|
|
|
|
volumes, _ := models.FindVolumesByAppID(
|
|
context.Background(), testDB, app.ID,
|
|
)
|
|
assert.Empty(t, volumes)
|
|
|
|
events, _ := models.FindWebhookEventsByAppID(
|
|
context.Background(), testDB, app.ID, 10,
|
|
)
|
|
assert.Empty(t, events)
|
|
|
|
deployments, _ := models.FindDeploymentsByAppID(
|
|
context.Background(), testDB, app.ID, 10,
|
|
)
|
|
assert.Empty(t, deployments)
|
|
})
|
|
}
|
|
|
|
// Resource Limits Tests.
|
|
|
|
//nolint:funlen // integration test with multiple subtests
|
|
func TestAppResourceLimits(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("saves and loads CPU limit", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testDB, cleanup := setupTestDB(t)
|
|
defer cleanup()
|
|
|
|
app := createTestApp(t, testDB)
|
|
|
|
app.CPULimit = sql.NullFloat64{Float64: 0.5, Valid: true}
|
|
|
|
err := app.Save(context.Background())
|
|
require.NoError(t, err)
|
|
|
|
found, err := models.FindApp(context.Background(), testDB, app.ID)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, found)
|
|
assert.True(t, found.CPULimit.Valid)
|
|
assert.InDelta(t, 0.5, found.CPULimit.Float64, 0.001)
|
|
})
|
|
|
|
t.Run("saves and loads memory limit", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testDB, cleanup := setupTestDB(t)
|
|
defer cleanup()
|
|
|
|
app := createTestApp(t, testDB)
|
|
|
|
app.MemoryLimit = sql.NullInt64{Int64: 536870912, Valid: true} // 512m
|
|
|
|
err := app.Save(context.Background())
|
|
require.NoError(t, err)
|
|
|
|
found, err := models.FindApp(context.Background(), testDB, app.ID)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, found)
|
|
assert.True(t, found.MemoryLimit.Valid)
|
|
assert.Equal(t, int64(536870912), found.MemoryLimit.Int64)
|
|
})
|
|
|
|
t.Run("null limits by default", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testDB, cleanup := setupTestDB(t)
|
|
defer cleanup()
|
|
|
|
app := createTestApp(t, testDB)
|
|
|
|
found, err := models.FindApp(context.Background(), testDB, app.ID)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, found)
|
|
assert.False(t, found.CPULimit.Valid)
|
|
assert.False(t, found.MemoryLimit.Valid)
|
|
})
|
|
|
|
t.Run("clears limits when set to null", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testDB, cleanup := setupTestDB(t)
|
|
defer cleanup()
|
|
|
|
app := createTestApp(t, testDB)
|
|
|
|
// Set limits
|
|
app.CPULimit = sql.NullFloat64{Float64: 1.0, Valid: true}
|
|
app.MemoryLimit = sql.NullInt64{Int64: 1073741824, Valid: true} // 1g
|
|
|
|
err := app.Save(context.Background())
|
|
require.NoError(t, err)
|
|
|
|
// Clear limits
|
|
app.CPULimit = sql.NullFloat64{}
|
|
app.MemoryLimit = sql.NullInt64{}
|
|
|
|
err = app.Save(context.Background())
|
|
require.NoError(t, err)
|
|
|
|
found, err := models.FindApp(context.Background(), testDB, app.ID)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, found)
|
|
assert.False(t, found.CPULimit.Valid)
|
|
assert.False(t, found.MemoryLimit.Valid)
|
|
})
|
|
}
|
|
|
|
// Helper function to create a test app.
|
|
func createTestApp(t *testing.T, testDB *database.Database) *models.App {
|
|
t.Helper()
|
|
|
|
app := models.NewApp(testDB)
|
|
app.ID = "test-app-" + t.Name()
|
|
app.Name = "test-app"
|
|
app.RepoURL = "git@example.com:user/repo.git"
|
|
app.Branch = testBranch
|
|
app.DockerfilePath = "Dockerfile"
|
|
app.WebhookSecret = "secret-" + t.Name()
|
|
app.WebhookSecretHash = database.HashWebhookSecret(app.WebhookSecret)
|
|
app.SSHPrivateKey = "private"
|
|
app.SSHPublicKey = "public"
|
|
|
|
err := app.Save(context.Background())
|
|
require.NoError(t, err)
|
|
|
|
return app
|
|
}
|