feat(suite): Implement Suites (#1239)

* feat(suite): Implement Suites

Fixes #1230

* Update docs

* Fix variable alignment

* Prevent always-run endpoint from running if a context placeholder fails to resolve in the URL

* Return errors when a context placeholder path fails to resolve

* Add a couple of unit tests

* Add a couple of unit tests

* fix(ui): Update group count properly

Fixes #1233

* refactor: Pass down entire config instead of several sub-configs

* fix: Change default suite interval and timeout

* fix: Deprecate disable-monitoring-lock in favor of concurrency

* fix: Make sure there are no duplicate keys

* Refactor some code

* Update watchdog/watchdog.go

* Update web/app/src/components/StepDetailsModal.vue

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* chore: Remove useless log

* fix: Set default concurrency to 3 instead of 5

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
TwiN
2025-09-05 15:39:12 -04:00
committed by GitHub
parent 10cabb9dde
commit d668a14703
74 changed files with 7513 additions and 652 deletions

View File

@@ -38,7 +38,8 @@ func (s *Store) createSQLiteSchema() error {
hostname TEXT NOT NULL,
ip TEXT NOT NULL,
duration INTEGER NOT NULL,
timestamp TIMESTAMP NOT NULL
timestamp TIMESTAMP NOT NULL,
suite_result_id INTEGER REFERENCES suite_results(suite_result_id) ON DELETE CASCADE
)
`)
if err != nil {
@@ -82,6 +83,32 @@ func (s *Store) createSQLiteSchema() error {
if err != nil {
return err
}
// Create suite tables
_, err = s.db.Exec(`
CREATE TABLE IF NOT EXISTS suites (
suite_id INTEGER PRIMARY KEY,
suite_key TEXT UNIQUE,
suite_name TEXT NOT NULL,
suite_group TEXT NOT NULL,
UNIQUE(suite_name, suite_group)
)
`)
if err != nil {
return err
}
_, err = s.db.Exec(`
CREATE TABLE IF NOT EXISTS suite_results (
suite_result_id INTEGER PRIMARY KEY,
suite_id INTEGER NOT NULL REFERENCES suites(suite_id) ON DELETE CASCADE,
success INTEGER NOT NULL,
errors TEXT NOT NULL,
duration INTEGER NOT NULL,
timestamp TIMESTAMP NOT NULL
)
`)
if err != nil {
return err
}
// Create indices for performance reasons
_, err = s.db.Exec(`
CREATE INDEX IF NOT EXISTS endpoint_results_endpoint_id_idx ON endpoint_results (endpoint_id);
@@ -98,7 +125,23 @@ func (s *Store) createSQLiteSchema() error {
_, err = s.db.Exec(`
CREATE INDEX IF NOT EXISTS endpoint_result_conditions_endpoint_result_id_idx ON endpoint_result_conditions (endpoint_result_id);
`)
if err != nil {
return err
}
// Create index for suite_results
_, err = s.db.Exec(`
CREATE INDEX IF NOT EXISTS suite_results_suite_id_idx ON suite_results (suite_id);
`)
if err != nil {
return err
}
// Silent table modifications TODO: Remove this in v6.0.0
_, _ = s.db.Exec(`ALTER TABLE endpoint_results ADD domain_expiration INTEGER NOT NULL DEFAULT 0`)
// Add suite_result_id to endpoint_results table for suite endpoint linkage
_, _ = s.db.Exec(`ALTER TABLE endpoint_results ADD suite_result_id INTEGER REFERENCES suite_results(suite_result_id) ON DELETE CASCADE`)
// Create index for suite_result_id
_, _ = s.db.Exec(`CREATE INDEX IF NOT EXISTS endpoint_results_suite_result_id_idx ON endpoint_results(suite_result_id)`)
// Note: SQLite doesn't support DROP COLUMN in older versions, so we skip this cleanup
// The suite_id column in endpoints table will remain but unused
return err
}