diff --git a/Makefile b/Makefile index b44c78b..1e69594 100644 --- a/Makefile +++ b/Makefile @@ -206,11 +206,11 @@ release: release-new release-linux-386 release-linux-amd64 release-linux-arm rel .PHONY: test test: - go test -race ./... + go test -v -race ./... .PHONY: test-integration test-integration: - go test -tags=integrity ./... + go test -v -tags=integration ./... .PHONY: test-all test-all: test test-integration diff --git a/README.md b/README.md index a3555b0..5362c4d 100644 --- a/README.md +++ b/README.md @@ -291,7 +291,7 @@ To run regular tests: make test ``` -To run integration tests, tagged with `// +build integrity`: +To run integration tests, tagged with `// +build integration`: ```bash make test-integration diff --git a/cli/cli.go b/cli/cli.go index 1b36cbf..63603b4 100644 --- a/cli/cli.go +++ b/cli/cli.go @@ -3,12 +3,16 @@ package cli import ( "context" "database/sql" + "errors" "fmt" - "log" "log/slog" "os" "runtime" + "github.com/golang-migrate/migrate/v4" + _ "github.com/golang-migrate/migrate/v4/database/sqlite3" + _ "github.com/golang-migrate/migrate/v4/source/file" + "github.com/golang-migrate/migrate/v4/source/iofs" schema "github.com/keygen-sh/keygen-relay/db" "github.com/keygen-sh/keygen-relay/internal/cmd" "github.com/keygen-sh/keygen-relay/internal/config" @@ -51,6 +55,7 @@ Version: if err != nil { return fmt.Errorf("failed to parse 'no-audit' flag: %v", err) } + cfg.License.EnabledAudit = !disableAudit // init database connection in PersistentPreRun hook for getting persistent flags @@ -59,6 +64,7 @@ Version: store, conn, err = initStore(ctx, cfg) if err != nil { slog.Error("failed to initialize store", "error", err) + return err } @@ -70,9 +76,11 @@ Version: if conn != nil { if err := conn.Close(); err != nil { slog.Error("failed to close database connection", "error", err) + return err } } + return nil }, } @@ -98,84 +106,129 @@ Version: } func initStore(ctx context.Context, cfg *config.Config) (licenses.Store, *sql.DB, error) { - dbExists := fileExists(cfg.DB.DatabaseFilePath) - dbConn, err := sql.Open("sqlite3", cfg.DB.DatabaseFilePath) - + conn, err := sql.Open("sqlite3", cfg.DB.DatabaseFilePath) if err != nil { slog.Error("failed to open database", "error", err) + return nil, nil, err } - if err := dbConn.Ping(); err != nil { + if err := conn.Ping(); err != nil { slog.Error("failed to connect to database", "error", err) + return nil, nil, err } slog.Info("applying database pragmas", "path", cfg.DB.DatabaseFilePath) // set the journal mode to Write-Ahead Logging for concurrency - if _, err := dbConn.Exec("PRAGMA journal_mode = WAL"); err != nil { - log.Fatalf("failed to set journal_mode pragma: %v", err) + if _, err := conn.Exec("PRAGMA journal_mode = WAL"); err != nil { + slog.Error("failed to set journal_mode pragma", "error", err) + + return nil, nil, err } // set synchronous mode to NORMAL to better balance performance and safety - if _, err := dbConn.Exec("PRAGMA synchronous = NORMAL"); err != nil { - log.Fatalf("failed to set synchronous pragma: %v", err) + if _, err := conn.Exec("PRAGMA synchronous = NORMAL"); err != nil { + slog.Error("failed to set synchronous pragma", "error", err) + + return nil, nil, err } // set busy timeout to 5 seconds to avoid lock-related errors - if _, err := dbConn.Exec("PRAGMA busy_timeout = 5000"); err != nil { - log.Fatalf("failed to set busy_timeout pragma: %v", err) + if _, err := conn.Exec("PRAGMA busy_timeout = 5000"); err != nil { + slog.Error("failed to set busy_timeout pragma", "error", err) + + return nil, nil, err } // set cache size to 20MB for faster data access - if _, err := dbConn.Exec("PRAGMA cache_size = -20000"); err != nil { - log.Fatalf("failed to set cache_size pragma: %v", err) + if _, err := conn.Exec("PRAGMA cache_size = -20000"); err != nil { + slog.Error("failed to set cache_size pragma", "error", err) + + return nil, nil, err } // enable foreign key constraints - if _, err := dbConn.Exec("PRAGMA foreign_keys = ON"); err != nil { - log.Fatalf("failed to set foreign_keys pragma: %v", err) + if _, err := conn.Exec("PRAGMA foreign_keys = ON"); err != nil { + slog.Error("failed to set foreign_keys pragma", "error", err) + + return nil, nil, err } // enable auto vacuuming and set it to incremental mode for gradual space reclaiming - if _, err := dbConn.Exec("PRAGMA auto_vacuum = INCREMENTAL"); err != nil { - log.Fatalf("failed to set auto_vacuum pragma: %v", err) + if _, err := conn.Exec("PRAGMA auto_vacuum = INCREMENTAL"); err != nil { + slog.Error("failed to set auto_vacuum pragma", "error", err) + + return nil, nil, err } // store temporary tables and data in memory for better performance - if _, err := dbConn.Exec("PRAGMA temp_store = MEMORY"); err != nil { - log.Fatalf("failed to set temp_store pragma: %v", err) + if _, err := conn.Exec("PRAGMA temp_store = MEMORY"); err != nil { + slog.Error("failed to set temp_store pragma", "error", err) + + return nil, nil, err } // set the mmap_size to 2GB for faster read/write access using memory-mapped I/O - if _, err := dbConn.Exec("PRAGMA mmap_size = 2147483648"); err != nil { - log.Fatalf("failed to set mmap_size pragma: %v", err) + if _, err := conn.Exec("PRAGMA mmap_size = 2147483648"); err != nil { + slog.Error("failed to set mmap_size pragma", "error", err) + + return nil, nil, err } // set the page size to 8KB for balanced memory usage and performance - if _, err := dbConn.Exec("PRAGMA page_size = 8192"); err != nil { - log.Fatalf("failed to set page_size pragma: %v", err) + if _, err := conn.Exec("PRAGMA page_size = 8192"); err != nil { + slog.Error("failed to set page_size pragma", "error", err) + + return nil, nil, err } - if !dbExists { + // apply migrations if database exists otherwise init and apply schema + if ok := schemaExists(conn); ok { + slog.Info("applying database migrations", "path", cfg.DB.DatabaseFilePath) + + migrations, err := iofs.New(schema.Migrations, "migrations") + if err != nil { + slog.Error("failed to initialize migrations fs", "error", err) + + return nil, nil, err + } + + migrator, err := db.NewMigrator(conn, migrations) + if err != nil { + slog.Error("failed to initialize migrations", "error", err) + + return nil, nil, err + } + + if err := migrator.Up(); err != nil && !errors.Is(err, migrate.ErrNoChange) { + slog.Error("failed to apply migrations", "error", err) + + return nil, nil, err + } + } else { slog.Info("applying database schema", "path", cfg.DB.DatabaseFilePath) - if _, err := dbConn.ExecContext(ctx, schema.SchemaSQL); err != nil { - slog.Error("failed to execute schema", "error", err) + if _, err := conn.ExecContext(ctx, schema.Schema); err != nil { + slog.Error("failed to apply schema", "error", err) + return nil, nil, err } } - queries := db.New(dbConn) - return db.NewStore(queries, dbConn), dbConn, nil + queries := db.New(conn) + store := db.NewStore(queries, conn) + + return store, conn, nil } -func fileExists(filename string) bool { - info, err := os.Stat(filename) - if err != nil { - slog.Debug("file does not exist", "filename", filename) - return false - } - return !info.IsDir() +func schemaExists(db *sql.DB) bool { + var t string + + // check if any tables exist + query := `SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' LIMIT 1` + err := db.QueryRow(query).Scan(&t) + + return err == nil } diff --git a/cli/cli_test.go b/cli/cli_test.go new file mode 100644 index 0000000..8d339d1 --- /dev/null +++ b/cli/cli_test.go @@ -0,0 +1,75 @@ +//go:build integration +// +build integration + +package cli_test + +import ( + "fmt" + "os" + "path/filepath" + "testing" + + "github.com/keygen-sh/keygen-relay/cli" + "github.com/rogpeppe/go-internal/testscript" +) + +func TestMain(m *testing.M) { + code := testscript.RunMain(m, map[string]func() int{ + "relay": func() int { + return cli.Run() + }, + }) + + os.Exit(code) +} + +func TestIntegration(t *testing.T) { + t.Parallel() + + testscript.Run(t, testscript.Params{ + Dir: "testdata", + RequireExplicitExec: true, + TestWork: true, + Setup: setup, + }) +} + +func setup(env *testscript.Env) error { + setupFixtures(env) + setupEnv(env) + + return nil +} + +func setupFixtures(env *testscript.Env) error { + fixtures := []string{ + "license.lic", + "license_2.lic", + } + + for _, fixture := range fixtures { + if err := copyFile(filepath.Join("testdata", fixture), filepath.Join(env.WorkDir, fixture)); err != nil { + return fmt.Errorf("failed to copy fixture %s: %w", fixture, err) + } + } + + return nil +} + +func setupEnv(env *testscript.Env) error { + // TODO(ezekg) make prestine env + return nil +} + +func copyFile(src, dst string) error { + input, err := os.ReadFile(src) + if err != nil { + return fmt.Errorf("unable to read file %s: %w", src, err) + } + + if err := os.WriteFile(dst, input, 0644); err != nil { + return fmt.Errorf("unable to write file to %s: %w", dst, err) + } + + return nil +} diff --git a/testdata/claim_conflict.txt b/cli/testdata/claim_conflict.test.txt similarity index 64% rename from testdata/claim_conflict.txt rename to cli/testdata/claim_conflict.test.txt index c384137..df9c3c6 100644 --- a/testdata/claim_conflict.txt +++ b/cli/testdata/claim_conflict.test.txt @@ -1,22 +1,22 @@ -# Add the license +# add the license exec relay add --file license.lic --key 9E32DD-D8CC22-771926-C2D834-C506DC-V3 --public-key e8601e48b69383ba520245fd07971e983d06d22c4257cfd82304601479cee788 -# Set a port as environment variable +# set a port as environment variable env PORT=65001 -# Start the server with heartbeat disabled +# start the server with heartbeat disabled exec relay serve --port $PORT --no-heartbeats &server_process_test& -# Wait for the server to start +# wait for the server to start exec sleep 1 -# Claim the license for the first time and check status code directly +# claim the license for the first time and check status code directly exec curl -s -o /dev/null -w "%{http_code}" -X PUT http://localhost:$PORT/v1/nodes/test_fingerprint stdout '201' -# Claim the license again with the same fingerprint and check status code directly +# claim the license again with the same fingerprint and check status code directly exec curl -s -o /dev/null -w "%{http_code}" -X PUT http://localhost:$PORT/v1/nodes/test_fingerprint stdout '409' -# Kill the process (stop the server) +# kill the process (stop the server) kill server_process_test diff --git a/testdata/claim_license_conflict.txt b/cli/testdata/claim_license_conflict.test.txt similarity index 56% rename from testdata/claim_license_conflict.txt rename to cli/testdata/claim_license_conflict.test.txt index ea2de7f..c37fb44 100644 --- a/testdata/claim_license_conflict.txt +++ b/cli/testdata/claim_license_conflict.test.txt @@ -1,26 +1,26 @@ -# Add the license +# add the license exec relay add --file license.lic --key 9E32DD-D8CC22-771926-C2D834-C506DC-V3 --public-key e8601e48b69383ba520245fd07971e983d06d22c4257cfd82304601479cee788 -# Set a port as environment variable +# set a port as environment variable env PORT=65002 -# Start the server with heartbeat disabled +# start the server with heartbeat disabled exec relay serve --port $PORT --no-heartbeats &server_process_test& -# Wait for the server to start +# wait for the server to start exec sleep 1 -# Claim the license for the first time +# claim the license for the first time exec curl -s -o /dev/null -w "%{http_code}" -X PUT http://localhost:$PORT/v1/nodes/test_fingerprint stdout '201' -# Claim the license again with the same fingerprint +# claim the license again with the same fingerprint exec curl -s -o response.txt -w "%{http_code}" -X PUT http://localhost:$PORT/v1/nodes/test_fingerprint -# Expect a conflict response with status code 409 and error message +# expect a conflict response with status code 409 and error message stdout '409' -exec grep '{"error":"License claim conflict, heartbeat disabled"}' response.txt +exec grep '{"error":"failed to claim license due to conflict"}' response.txt -# Kill the process (stop the server) +# kill the process (stop the server) kill server_process_test diff --git a/testdata/claim_license_extended.txt b/cli/testdata/claim_license_extended.test.txt similarity index 63% rename from testdata/claim_license_extended.txt rename to cli/testdata/claim_license_extended.test.txt index 37fffc8..db59a18 100644 --- a/testdata/claim_license_extended.txt +++ b/cli/testdata/claim_license_extended.test.txt @@ -1,22 +1,22 @@ -# Add the license +# add the license exec relay add --file license.lic --key 9E32DD-D8CC22-771926-C2D834-C506DC-V3 --public-key e8601e48b69383ba520245fd07971e983d06d22c4257cfd82304601479cee788 -# Set a port as environment variable +# set a port as environment variable env PORT=65003 -# Start the server with heartbeat enabled (for extension to work) +# start the server with heartbeat enabled (for extension to work) exec relay serve --port $PORT &server_process_test& -# Wait for the server to start +# wait for the server to start exec sleep 1 -# Claim a license for the first time +# claim a license for the first time exec curl -s -o /dev/null -w "%{http_code}" -X PUT http://localhost:$PORT/v1/nodes/test_fingerprint stdout '201' -# Claim the license again with the same fingerprint to trigger an extension +# claim the license again with the same fingerprint to trigger an extension exec curl -s -o response.txt -w "%{http_code}" -X PUT http://localhost:$PORT/v1/nodes/test_fingerprint stdout '202' -# Kill the process (stop the server) +# kill the process (stop the server) kill server_process_test diff --git a/testdata/claim_license_lifo_strategy.txt b/cli/testdata/claim_license_lifo_strategy.test.txt similarity index 67% rename from testdata/claim_license_lifo_strategy.txt rename to cli/testdata/claim_license_lifo_strategy.test.txt index af07480..be6e487 100644 --- a/testdata/claim_license_lifo_strategy.txt +++ b/cli/testdata/claim_license_lifo_strategy.test.txt @@ -1,28 +1,28 @@ -# Add the first license +# add the first license exec relay add --file license.lic --key 9E32DD-D8CC22-771926-C2D834-C506DC-V3 --public-key e8601e48b69383ba520245fd07971e983d06d22c4257cfd82304601479cee788 -# Give a pause between adding licenses +# give a pause between adding licenses exec sleep 1 -# Add the second license +# add the second license exec relay add --file license_2.lic --key 9A96B8-FD08CD-8C433B-7657C8-8A8655-V3 --public-key e8601e48b69383ba520245fd07971e983d06d22c4257cfd82304601479cee788 -# Set a port and strategy as environment variables +# set a port and strategy as environment variables env PORT=65004 env STRATEGY=lifo -# Start the server with FIFO strategy +# start the server with FIFO strategy exec relay serve --port $PORT --strategy $STRATEGY &server_process_test& -# Wait for the server to start +# wait for the server to start exec sleep 1 -# Claim a license (LIFO: should return the last license) +# claim a license (LIFO: should return the last license) exec curl -s -o response.txt -w "%{http_code}" -X PUT http://localhost:$PORT/v1/nodes/test_fingerprint -# Expect the first license to be returned +# expect the first license to be returned exec grep '"license_file":' response.txt exec grep '"license_key":"9A96B8-FD08CD-8C433B-7657C8-8A8655-V3"' response.txt -# Kill the process (stop the server) +# kill the process (stop the server) kill server_process_test diff --git a/testdata/claim_license_success.txt b/cli/testdata/claim_license_success.test.txt similarity index 62% rename from testdata/claim_license_success.txt rename to cli/testdata/claim_license_success.test.txt index ce1309a..a6b3e21 100644 --- a/testdata/claim_license_success.txt +++ b/cli/testdata/claim_license_success.test.txt @@ -1,26 +1,26 @@ -# Add the license +# add the license exec relay add --file license.lic --key 9E32DD-D8CC22-771926-C2D834-C506DC-V3 --public-key e8601e48b69383ba520245fd07971e983d06d22c4257cfd82304601479cee788 -# Set a port as environment variable +# set a port as environment variable env PORT=65005 -# Start the server with heartbeat disabled +# start the server with heartbeat disabled exec relay serve --port $PORT &server_process_test& -# Wait for the server to start +# wait for the server to start exec sleep 1 -# Claim a license +# claim a license exec curl -s -o response.txt -w "%{http_code}" -X PUT http://localhost:$PORT/v1/nodes/test_fingerprint -# Expect a success response with status code 201 +# expect a success response with status code 201 stdout '201' -# Check that the response contains license_file and license_key fields (using grep) +# check that the response contains license_file and license_key fields (using grep) exec grep '"license_file":' response.txt exec grep '"license_key":' response.txt -# Kill the process (stop the server) +# kill the process (stop the server) kill server_process_test diff --git a/cli/testdata/claim_no_licenses_available.test.txt b/cli/testdata/claim_no_licenses_available.test.txt new file mode 100644 index 0000000..432d672 --- /dev/null +++ b/cli/testdata/claim_no_licenses_available.test.txt @@ -0,0 +1,16 @@ +# start the server without adding any licenses +env PORT=65006 +exec relay serve --port $PORT &server_process_test& + +# wait for the server to start +exec sleep 1 + +# attempt to claim a license when none are available +exec curl -s -o response.txt -w "%{http_code}" -X PUT http://localhost:$PORT/v1/nodes/test_fingerprint + +# expect a gone response with status code 410 and error message +stdout '410' +exec grep 'no licenses available' response.txt + +# kill the process (stop the server) +kill server_process_test diff --git a/testdata/cmd_add.txt b/cli/testdata/cmd_add.test.txt similarity index 60% rename from testdata/cmd_add.txt rename to cli/testdata/cmd_add.test.txt index 7d8052d..ced056b 100644 --- a/testdata/cmd_add.txt +++ b/cli/testdata/cmd_add.test.txt @@ -1,11 +1,11 @@ -# Add the license +# add the license exec relay add --file license.lic --key 9E32DD-D8CC22-771926-C2D834-C506DC-V3 --public-key e8601e48b69383ba520245fd07971e983d06d22c4257cfd82304601479cee788 -# Expect output indicating success -stdout 'License added successfully.' +# expect output indicating success +stdout 'license added successfully' -# List licenses to confirm +# list licenses to confirm exec relay ls --plain -# Expect the license to be listed +# expect the license to be listed stdout 'dcea31a4-1664-4633-9f52-4a1b0b5ea2ef' diff --git a/testdata/cmd_del.txt b/cli/testdata/cmd_del.test.txt similarity index 60% rename from testdata/cmd_del.txt rename to cli/testdata/cmd_del.test.txt index 3a9a7c6..d401d2f 100644 --- a/testdata/cmd_del.txt +++ b/cli/testdata/cmd_del.test.txt @@ -1,16 +1,16 @@ -# Add the license +# add the license exec relay add --file license.lic --key 9E32DD-D8CC22-771926-C2D834-C506DC-V3 --public-key e8601e48b69383ba520245fd07971e983d06d22c4257cfd82304601479cee788 -# Confirm the license is added +# confirm the license is added exec relay ls --plain stdout 'dcea31a4-1664-4633-9f52-4a1b0b5ea2ef' -# Delete the license +# delete the license exec relay del --license dcea31a4-1664-4633-9f52-4a1b0b5ea2ef -# Expect output indicating success -stdout 'License deleted successfully.' +# expect output indicating success +stdout 'license deleted successfully' -# Verify the license is deleted +# verify the license is deleted exec relay ls --plain -stdout 'No licenses found.' +stdout 'no licenses found' diff --git a/testdata/cmd_serve.txt b/cli/testdata/cmd_serve.test.txt similarity index 55% rename from testdata/cmd_serve.txt rename to cli/testdata/cmd_serve.test.txt index 25cf3fb..e010324 100644 --- a/testdata/cmd_serve.txt +++ b/cli/testdata/cmd_serve.test.txt @@ -1,26 +1,26 @@ -# Add the license +# add the license exec relay add --file license.lic --key 9E32DD-D8CC22-771926-C2D834-C506DC-V3 --public-key e8601e48b69383ba520245fd07971e983d06d22c4257cfd82304601479cee788 -# Set a port as environment variable +# set a port as environment variable env PORT=65007 -# Start the server in the background with the environment variable +# start the server in the background with the environment variable exec relay serve --port $PORT &server_process& -# Wait for the server to start +# wait for the server to start exec sleep 1 -# Claim a license using the port from the environment variable +# claim a license using the port from the environment variable exec curl -s -X PUT http://localhost:$PORT/v1/nodes/test_fingerprint -# Expect license data in the response +# expect license data in the response stdout '"license_file":' -# Release the license +# release the license exec curl -s -X DELETE http://localhost:$PORT/v1/nodes/test_fingerprint -# Expect no content in the response +# expect no content in the response stdout '' -# Kill the process (stop the server) +# kill the process (stop the server) kill server_process diff --git a/testdata/cmd_stat.txt b/cli/testdata/cmd_stat.test.txt similarity index 73% rename from testdata/cmd_stat.txt rename to cli/testdata/cmd_stat.test.txt index c951622..b810e89 100644 --- a/testdata/cmd_stat.txt +++ b/cli/testdata/cmd_stat.test.txt @@ -1,8 +1,8 @@ -# Add the license +# add the license exec relay add --file license.lic --key 9E32DD-D8CC22-771926-C2D834-C506DC-V3 --public-key e8601e48b69383ba520245fd07971e983d06d22c4257cfd82304601479cee788 -# Get statistics of the license +# get statistics of the license exec relay stat --license dcea31a4-1664-4633-9f52-4a1b0b5ea2ef --plain -# Expect output containing the license details +# expect output containing the license details stdout 'dcea31a4-1664-4633-9f52-4a1b0b5ea2ef' diff --git a/cli/testdata/cmd_version.test.txt b/cli/testdata/cmd_version.test.txt new file mode 100644 index 0000000..077887d --- /dev/null +++ b/cli/testdata/cmd_version.test.txt @@ -0,0 +1,4 @@ +exec relay version + +# should use the dev version +stdout '' diff --git a/testdata/license.lic b/cli/testdata/license.lic similarity index 100% rename from testdata/license.lic rename to cli/testdata/license.lic diff --git a/testdata/license_2.lic b/cli/testdata/license_2.lic similarity index 100% rename from testdata/license_2.lic rename to cli/testdata/license_2.lic diff --git a/cli/testdata/release_license_not_found.test.txt b/cli/testdata/release_license_not_found.test.txt new file mode 100644 index 0000000..1ee78b9 --- /dev/null +++ b/cli/testdata/release_license_not_found.test.txt @@ -0,0 +1,16 @@ +# start the server +env PORT=65008 +exec relay serve --port $PORT &server_process_test& + +# wait for the server to start +exec sleep 1 + +# attempt to release a license that was never claimed +exec curl -s -o response.txt -w "%{http_code}" -X DELETE http://localhost:$PORT/v1/nodes/test_fingerprint + +# expect a not found response with status code 404 and error message +stdout '404' +exec grep '{"error":"claim not found"}' response.txt + +# kill the process (stop the server) +kill server_process_test diff --git a/testdata/release_license_success.txt b/cli/testdata/release_license_success.test.txt similarity index 70% rename from testdata/release_license_success.txt rename to cli/testdata/release_license_success.test.txt index 720c9b8..a17e482 100644 --- a/testdata/release_license_success.txt +++ b/cli/testdata/release_license_success.test.txt @@ -1,22 +1,22 @@ -# Add the license +# add the license exec relay add --file license.lic --key 9E32DD-D8CC22-771926-C2D834-C506DC-V3 --public-key e8601e48b69383ba520245fd07971e983d06d22c4257cfd82304601479cee788 -# Set a port as environment variable +# set a port as environment variable env PORT=65009 -# Start the server with heartbeat disabled +# start the server with heartbeat disabled exec relay serve --port $PORT &server_process_test& -# Wait for the server to start +# wait for the server to start exec sleep 1 -# Claim a license +# claim a license exec curl -s -o /dev/null -w "%{http_code}" -X PUT http://localhost:$PORT/v1/nodes/test_fingerprint stdout '201' -# Release the license +# release the license exec curl -s -o /dev/null -w "%{http_code}" -X DELETE http://localhost:$PORT/v1/nodes/test_fingerprint stdout '204' -# Kill the process (stop the server) +# kill the process (stop the server) kill server_process_test diff --git a/testdata/server_database.txt b/cli/testdata/server_database.test.txt similarity index 58% rename from testdata/server_database.txt rename to cli/testdata/server_database.test.txt index f599320..6aa5344 100644 --- a/testdata/server_database.txt +++ b/cli/testdata/server_database.test.txt @@ -1,32 +1,32 @@ -# Set a custom database path +# set a custom database path env DATABASE_PATH=custom_relay.sqlite -# Remove any existing custom database +# remove any existing custom database rm -f $DATABASE_PATH -# Add a license with a custom database path +# add a license with a custom database path exec relay add --file license.lic --key 9E32DD-D8CC22-771926-C2D834-C506DC-V3 --public-key e8601e48b69383ba520245fd07971e983d06d22c4257cfd82304601479cee788 --database $DATABASE_PATH -# Ensure that the custom database is created +# ensure that the custom database is created exec test -f $DATABASE_PATH -# Set a port as environment variable +# set a port as environment variable env PORT=65010 -# Start the server with custom database path +# start the server with custom database path exec relay serve --port $PORT --database $DATABASE_PATH &server_process_test& -# Wait for the server to start +# wait for the server to start exec sleep 1 -# Claim a license +# claim a license exec curl -s -o response.txt -w "%{http_code}" -X PUT http://localhost:$PORT/v1/nodes/test_fingerprint -# Expect a success response (status 201) +# expect a success response (status 201) stdout '201' -# Kill the process (stop the server) +# kill the process (stop the server) kill server_process_test -# Clean up the custom database +# clean up the custom database rm -f $DATABASE_PATH diff --git a/db/embed.go b/db/embed.go index 825fac7..7d39483 100644 --- a/db/embed.go +++ b/db/embed.go @@ -1,6 +1,11 @@ package schema -import _ "embed" +import ( + "embed" +) //go:embed schema.sql -var SchemaSQL string +var Schema string + +//go:embed migrations/*.sql +var Migrations embed.FS diff --git a/db/migrations/1733523440_add_migrations.down.sql b/db/migrations/1733523440_add_migrations.down.sql new file mode 100644 index 0000000..e69de29 diff --git a/db/migrations/1733523440_add_migrations.up.sql b/db/migrations/1733523440_add_migrations.up.sql new file mode 100644 index 0000000..e69de29 diff --git a/go.mod b/go.mod index e4dd66a..990c2ed 100644 --- a/go.mod +++ b/go.mod @@ -3,10 +3,12 @@ module github.com/keygen-sh/keygen-relay go 1.23.1 require ( + github.com/bmatcuk/doublestar/v4 v4.7.1 github.com/charmbracelet/bubbles v0.20.0 github.com/charmbracelet/bubbletea v1.1.1 github.com/charmbracelet/lipgloss v0.13.0 github.com/fatih/color v1.17.0 + github.com/golang-migrate/migrate/v4 v4.18.1 github.com/gorilla/mux v1.8.1 github.com/keygen-sh/keygen-go/v3 v3.2.0 github.com/lmittmann/tint v1.0.5 @@ -24,8 +26,9 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/google/go-querystring v1.1.0 // indirect - github.com/google/uuid v1.6.0 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.1 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/keygen-sh/go-update v1.0.0 // indirect github.com/keygen-sh/jsonapi-go v1.2.1 // indirect @@ -36,15 +39,16 @@ require ( github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect github.com/muesli/cancelreader v0.2.2 // indirect github.com/muesli/termenv v0.15.2 // indirect + github.com/nxadm/tail v1.4.11 // indirect github.com/oasisprotocol/curve25519-voi v0.0.0-20211102120939-d5a936accd94 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/spf13/pflag v1.0.5 // indirect + go.uber.org/atomic v1.7.0 // indirect golang.org/x/crypto v0.27.0 // indirect - golang.org/x/net v0.29.0 // indirect golang.org/x/sync v0.8.0 // indirect golang.org/x/sys v0.25.0 // indirect golang.org/x/text v0.18.0 // indirect - golang.org/x/tools v0.22.0 // indirect + golang.org/x/tools v0.24.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 798a863..f524ba7 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,8 @@ github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiE github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8= github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA= +github.com/bmatcuk/doublestar/v4 v4.7.1 h1:fdDeAqgT47acgwd9bd9HxJRDmc9UAmPpc+2m0CXv75Q= +github.com/bmatcuk/doublestar/v4 v4.7.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= github.com/charmbracelet/bubbles v0.20.0 h1:jSZu6qD8cRQ6k9OMfR1WlM+ruM8fkPWkHvQWD9LIutE= github.com/charmbracelet/bubbles v0.20.0/go.mod h1:39slydyswPy+uVOHZ5x/GjwVAFkCsV8IIVy+4MhzwwU= github.com/charmbracelet/bubbletea v1.1.1 h1:KJ2/DnmpfqFtDNVTvYZ6zpPFL9iRCRr0qqKOCvppbPY= @@ -15,6 +17,7 @@ github.com/charmbracelet/x/exp/golden v0.0.0-20240815200342-61de596daa2b/go.mod github.com/charmbracelet/x/term v0.2.0 h1:cNB9Ot9q8I711MyZ7myUR5HFWL/lc3OpU8jZ4hwm0x0= github.com/charmbracelet/x/term v0.2.0/go.mod h1:GVxgxAbjUrmpvIINHIQnJJKpMlHiZ4cktEQCN6GWyF0= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/denisbrodbeck/machineid v1.0.1 h1:geKr9qtkB876mXguW2X6TU4ZynleN6ezuMSRhl4D7AQ= @@ -24,6 +27,10 @@ github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97 github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/golang-migrate/migrate/v4 v4.18.1 h1:JML/k+t4tpHCpQTCAD62Nu43NUFzHY4CV3uAuvHGC+Y= +github.com/golang-migrate/migrate/v4 v4.18.1/go.mod h1:HAX6m3sQgcdO81tdjn5exv20+3Kb13cmGli1hrD6hks= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= @@ -34,11 +41,15 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-retryablehttp v0.7.1 h1:sUiuQAnLlbvmExtFQs72iFW/HXeUn8Z1aJLQ4LJJbTQ= github.com/hashicorp/go-retryablehttp v0.7.1/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= -github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= @@ -48,6 +59,8 @@ github.com/keygen-sh/jsonapi-go v1.2.1 h1:NTSIAxl2+7S5fPnKgrYwNjQSWbdKRtrFq26SD8 github.com/keygen-sh/jsonapi-go v1.2.1/go.mod h1:8j9vsLiKyJyDqmt8r3tYaYNmXszq2+cFhoO6QdMdAes= github.com/keygen-sh/keygen-go/v3 v3.2.0 h1:OJqnGtY6z4ZA434kZqfNVHDmSrN5zq4l4XItcB3tECY= github.com/keygen-sh/keygen-go/v3 v3.2.0/go.mod h1:YoFyryzXEk6XrbT3H8EUUU+JcIJkQu414TA6CvZgS/E= +github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lmittmann/tint v1.0.5 h1:NQclAutOfYsqs2F1Lenue6OoWCajs5wJcP3DfWVpePw= github.com/lmittmann/tint v1.0.5/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= @@ -69,14 +82,18 @@ github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELU github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= +github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY= +github.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc= github.com/oasisprotocol/curve25519-voi v0.0.0-20211102120939-d5a936accd94 h1:YXfl+eCNmAQhVbSNQ85bSi1n4qhUBPW8Qq9Rac4pt/s= github.com/oasisprotocol/curve25519-voi v0.0.0-20211102120939-d5a936accd94/go.mod h1:WUcXjUd98qaCVFb6j8Xc87MsKeMCXDu9Nk8JRJ9SeC8= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU= github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= +github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.9.0 h1:R1uwffexN6Pr340GtYRIdZmAiN4J+iw6WG4wog1DUXg= github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= +github.com/onsi/gomega v1.15.0 h1:WjP/FQ/sk43MRmnEcT+MlDw2TFvkrXlprrPST/IudjU= +github.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= @@ -89,8 +106,12 @@ github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210813211128-0a44fdfbc16e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= @@ -111,6 +132,7 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= @@ -121,14 +143,12 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= -golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= +golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= +golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= diff --git a/integrity_test.go b/integrity_test.go deleted file mode 100644 index 7ca39f9..0000000 --- a/integrity_test.go +++ /dev/null @@ -1,56 +0,0 @@ -//go:build integrity -// +build integrity - -package main_test - -import ( - "fmt" - "github.com/rogpeppe/go-internal/testscript" - "os" - "os/exec" - "path/filepath" - "testing" -) - -func TestRelayIntegration(t *testing.T) { - testscript.Run(t, testscript.Params{ - Dir: "testdata", - Setup: setup, - }) -} - -// setup prepares the test environment by building the relay binary and copying required license files -func setup(env *testscript.Env) error { - binPath := filepath.Join(env.WorkDir, "relay") - buildCmd := exec.Command("go", "build", "-o", binPath, "./cmd/relay") - buildCmd.Stderr = os.Stderr - if err := buildCmd.Run(); err != nil { - return fmt.Errorf("failed to build relay binary: %w", err) - } - - env.Setenv("PATH", env.Getenv("PATH")+string(os.PathListSeparator)+env.WorkDir) - - // copy license files into the working directory tests - testscriptRoot := env.Getenv("TESTSCRIPT_ROOT") - if err := copyFile(filepath.Join(testscriptRoot, "testdata", "license.lic"), filepath.Join(env.WorkDir, "license.lic")); err != nil { - return fmt.Errorf("failed to copy license.lic: %w", err) - } - if err := copyFile(filepath.Join(testscriptRoot, "testdata", "license_2.lic"), filepath.Join(env.WorkDir, "license_2.lic")); err != nil { - return fmt.Errorf("failed to copy license_2.lic: %w", err) - } - - return nil -} - -func copyFile(src, dst string) error { - input, err := os.ReadFile(src) - if err != nil { - return fmt.Errorf("unable to read file %s: %w", src, err) - } - - if err := os.WriteFile(dst, input, 0644); err != nil { - return fmt.Errorf("unable to write file to %s: %w", dst, err) - } - - return nil -} diff --git a/internal/cmd/add.go b/internal/cmd/add.go index 4741945..9d7a40b 100644 --- a/internal/cmd/add.go +++ b/internal/cmd/add.go @@ -26,7 +26,7 @@ func AddCmd(manager licenses.Manager) *cobra.Command { return nil } - output.PrintSuccess(cmd.OutOrStdout(), "License added successfully.") + output.PrintSuccess(cmd.OutOrStdout(), "license added successfully") return nil }, diff --git a/internal/cmd/add_test.go b/internal/cmd/add_test.go index 3785336..698fcc6 100644 --- a/internal/cmd/add_test.go +++ b/internal/cmd/add_test.go @@ -30,7 +30,7 @@ func TestAddCmd_Success(t *testing.T) { err := addCmd.Execute() assert.NoError(t, err) - assert.Contains(t, outBuf.String(), "License added successfully.") + assert.Contains(t, outBuf.String(), "license added successfully") } func TestAddCmd_Error(t *testing.T) { @@ -51,7 +51,7 @@ func TestAddCmd_Error(t *testing.T) { _ = addCmd.Execute() - assert.Contains(t, errBuf.String(), "Error: failed to add license") + assert.Contains(t, errBuf.String(), "error: failed to add license") } func TestAddCmd_MissingRequiredFlags(t *testing.T) { diff --git a/internal/cmd/del.go b/internal/cmd/del.go index c1c02cd..23dc2f7 100644 --- a/internal/cmd/del.go +++ b/internal/cmd/del.go @@ -20,7 +20,7 @@ func DelCmd(manager licenses.Manager) *cobra.Command { return nil } - output.PrintSuccess(cmd.OutOrStdout(), "License deleted successfully.") + output.PrintSuccess(cmd.OutOrStdout(), "license deleted successfully") return nil }, diff --git a/internal/cmd/del_test.go b/internal/cmd/del_test.go index 2ecf16b..9ec84de 100644 --- a/internal/cmd/del_test.go +++ b/internal/cmd/del_test.go @@ -50,7 +50,7 @@ func TestDelCmd_Error(t *testing.T) { _ = delCmd.Execute() - assert.Contains(t, errBuf.String(), "Error: failed to remove license") + assert.Contains(t, errBuf.String(), "error: failed to remove license") } func TestDelCmd_MissingID(t *testing.T) { diff --git a/internal/cmd/ls.go b/internal/cmd/ls.go index fe3f941..425dcbb 100644 --- a/internal/cmd/ls.go +++ b/internal/cmd/ls.go @@ -29,7 +29,7 @@ func LsCmd(manager licenses.Manager) *cobra.Command { } if len(licensesList) == 0 { - output.PrintSuccess(cmd.OutOrStdout(), "No licenses found.") + output.PrintSuccess(cmd.OutOrStdout(), "no licenses found") return nil } @@ -67,7 +67,7 @@ func LsCmd(manager licenses.Manager) *cobra.Command { } if err := renderer.Render(tableRows, columns); err != nil { - output.PrintError(cmd.ErrOrStderr(), fmt.Sprintf("Error rendering table: %v", err)) + output.PrintError(cmd.ErrOrStderr(), fmt.Sprintf("error rendering table: %v", err)) return err } diff --git a/internal/cmd/ls_test.go b/internal/cmd/ls_test.go index 368c451..ba3f2f6 100644 --- a/internal/cmd/ls_test.go +++ b/internal/cmd/ls_test.go @@ -4,9 +4,10 @@ import ( "bytes" "context" "errors" + "testing" + "github.com/keygen-sh/keygen-relay/internal/licenses" "github.com/keygen-sh/keygen-relay/internal/testutils" - "testing" "github.com/keygen-sh/keygen-relay/internal/cmd" "github.com/stretchr/testify/assert" @@ -51,7 +52,7 @@ func TestLsCmd_NoLicenses(t *testing.T) { err := lsCmd.Execute() assert.NoError(t, err) - assert.Contains(t, outBuf.String(), "No licenses found.") + assert.Contains(t, outBuf.String(), "no licenses found") } func TestLsCmd_Error(t *testing.T) { diff --git a/internal/cmd/serve.go b/internal/cmd/serve.go index 542c7bb..97922b4 100644 --- a/internal/cmd/serve.go +++ b/internal/cmd/serve.go @@ -23,7 +23,7 @@ func ServeCmd(srv server.Server) *cobra.Command { router.Walk(func(route *mux.Route, router *mux.Router, ancestors []*mux.Route) error { path, _ := route.GetPathTemplate() - slog.Debug("Route registered", "path", path) + slog.Debug("route registered", "path", path) return nil }) @@ -38,7 +38,7 @@ func ServeCmd(srv server.Server) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { noHeartbeats, err := cmd.Flags().GetBool("no-heartbeats") if err != nil { - output.PrintError(cmd.ErrOrStderr(), fmt.Sprintf("Failed to parse 'no-heartbeats' flag: %v", err)) + output.PrintError(cmd.ErrOrStderr(), fmt.Sprintf("failed to parse 'no-heartbeats' flag: %v", err)) return err } @@ -46,7 +46,7 @@ func ServeCmd(srv server.Server) *cobra.Command { ttl, err := cmd.Flags().GetDuration("ttl") if err != nil { - output.PrintError(cmd.ErrOrStderr(), fmt.Sprintf("Failed to parse 'ttl' flag: %v", err)) + output.PrintError(cmd.ErrOrStderr(), fmt.Sprintf("failed to parse 'ttl' flag: %v", err)) return err } @@ -58,7 +58,7 @@ func ServeCmd(srv server.Server) *cobra.Command { srv.Manager().Config().Strategy = string(cfg.Strategy) srv.Manager().Config().ExtendOnHeartbeat = cfg.EnabledHeartbeat - output.PrintSuccess(cmd.OutOrStdout(), "The server is starting") + output.PrintSuccess(cmd.OutOrStdout(), "the server is starting") if err := srv.Run(); err != nil { output.PrintError(cmd.ErrOrStderr(), err.Error()) @@ -91,7 +91,7 @@ func ServeCmd(srv server.Server) *cobra.Command { func validateTTL(ttl time.Duration) error { if ttl < minTTL { - return fmt.Errorf("TTL value must be at least %s", minTTL) + return fmt.Errorf("time-to-live value must be at least %s", minTTL) } return nil } diff --git a/internal/cmd/serve_test.go b/internal/cmd/serve_test.go index c411f14..e576324 100644 --- a/internal/cmd/serve_test.go +++ b/internal/cmd/serve_test.go @@ -34,7 +34,7 @@ func TestServeCmd_Defaults(t *testing.T) { err := serveCmd.Execute() assert.NoError(t, err) - assert.Contains(t, output.String(), "The server is starting") + assert.Contains(t, output.String(), "the server is starting") assert.True(t, mockServer.RunCalled) assert.Equal(t, 6349, cfg.ServerPort) assert.Equal(t, 30*time.Second, cfg.TTL) @@ -77,7 +77,7 @@ func TestServeCmd_Flags(t *testing.T) { err := serveCmd.Execute() assert.NoError(t, err) - assert.Contains(t, output.String(), "The server is starting") + assert.Contains(t, output.String(), "the server is starting") assert.True(t, mockServer.RunCalled) assert.Equal(t, 9090, cfg.Server.ServerPort) assert.Equal(t, 1*time.Minute, cfg.Server.TTL) @@ -137,7 +137,7 @@ func TestServeCmd_RunError(t *testing.T) { _ = serveCmd.Execute() - assert.Contains(t, output.String(), "Error: failed to start server") + assert.Contains(t, output.String(), "error: failed to start server") assert.True(t, mockServer.RunCalled) } @@ -165,6 +165,6 @@ func TestServeCmd_InvalidTTL(t *testing.T) { _ = serveCmd.Execute() - assert.Contains(t, output.String(), "TTL value must be at least 30s") + assert.Contains(t, output.String(), "time-to-live value must be at least 30s") assert.False(t, mockServer.RunCalled) } diff --git a/internal/cmd/stat.go b/internal/cmd/stat.go index 9bda690..6301bed 100644 --- a/internal/cmd/stat.go +++ b/internal/cmd/stat.go @@ -60,7 +60,7 @@ func StatCmd(manager licenses.Manager) *cobra.Command { } if err := renderer.Render(tableRows, columns); err != nil { - output.PrintError(cmd.ErrOrStderr(), fmt.Sprintf("Error rendering table: %v", err)) + output.PrintError(cmd.ErrOrStderr(), fmt.Sprintf("error rendering table: %v", err)) return nil } diff --git a/internal/db/migrator.go b/internal/db/migrator.go new file mode 100644 index 0000000..5459e71 --- /dev/null +++ b/internal/db/migrator.go @@ -0,0 +1,42 @@ +package db + +import ( + "database/sql" + "fmt" + + "github.com/golang-migrate/migrate/v4" + "github.com/golang-migrate/migrate/v4/database/sqlite3" + "github.com/golang-migrate/migrate/v4/source" + _ "github.com/mattn/go-sqlite3" +) + +type Migrator struct { + migrate *migrate.Migrate +} + +func (m Migrator) Up() error { + return m.migrate.Up() +} + +func (m Migrator) Down() error { + return m.migrate.Down() +} + +func NewMigrator(db *sql.DB, migrations source.Driver) (*Migrator, error) { + instance, err := sqlite3.WithInstance(db, &sqlite3.Config{}) + if err != nil { + return nil, err + } + + m, err := migrate.NewWithInstance( + "file", + migrations, + "sqlite3", + instance, + ) + if err != nil { + return nil, fmt.Errorf("failed to create migrator: %w", err) + } + + return &Migrator{migrate: m}, nil +} diff --git a/internal/licenses/manager.go b/internal/licenses/manager.go index 45bffb4..97568fb 100644 --- a/internal/licenses/manager.go +++ b/internal/licenses/manager.go @@ -118,7 +118,7 @@ func (m *manager) AttachStore(store Store) { } func (m *manager) AddLicense(ctx context.Context, licenseFilePath string, licenseKey string, publicKey string) error { - slog.Debug("Starting to add a new license", "filePath", licenseFilePath) + slog.Debug("starting to add a new license", "filePath", licenseFilePath) cert, err := m.dataReader(licenseFilePath) if err != nil { @@ -131,7 +131,7 @@ func (m *manager) AddLicense(ctx context.Context, licenseFilePath string, licens return fmt.Errorf("failed to read license file: %w", err) } - slog.Debug("Successfully read the license file", "filePath", licenseFilePath) + slog.Debug("successfully read the license file", "filePath", licenseFilePath) lic := m.verifier(cert) keygen.PublicKey = publicKey @@ -149,7 +149,7 @@ func (m *manager) AddLicense(ctx context.Context, licenseFilePath string, licens key := dec.License.Key if err := m.store.InsertLicense(ctx, id, cert, key); err != nil { - slog.Debug("Failed to insert license", "licenseID", id, "error", err) + slog.Debug("failed to insert license", "licenseID", id, "error", err) if isUniqueConstraintError(err) { return fmt.Errorf("license with the provided key already exists") @@ -171,12 +171,12 @@ func (m *manager) AddLicense(ctx context.Context, licenseFilePath string, licens } func (m *manager) RemoveLicense(ctx context.Context, id string) error { - slog.Debug("Starting to remove license", "id", id) + slog.Debug("starting to remove license", "id", id) err := m.store.DeleteLicenseByIDTx(ctx, id) if err != nil { if errors.Is(err, sql.ErrNoRows) { - return fmt.Errorf("license with ID %s not found", id) + return fmt.Errorf("license %s not found", id) } slog.Debug("failed to delete license", "licenseID", id, "error", err) return fmt.Errorf("failed to delete license: %w", err) @@ -194,7 +194,7 @@ func (m *manager) RemoveLicense(ctx context.Context, id string) error { } func (m *manager) ListLicenses(ctx context.Context) ([]License, error) { - slog.Debug("Fetching licenses") + slog.Debug("fetching licenses") licenses, err := m.store.GetAllLicenses(ctx) if err != nil { @@ -212,13 +212,13 @@ func (m *manager) ListLicenses(ctx context.Context) ([]License, error) { } func (m *manager) GetLicenseByID(ctx context.Context, id string) (License, error) { - slog.Debug("Fetching license", "id", id) + slog.Debug("fetching license", "id", id) license, err := m.store.GetLicenseByID(ctx, id) if err != nil { if errors.Is(err, sql.ErrNoRows) { slog.Debug("license not found", "licenseID", id) - return License{}, fmt.Errorf("license with ID %s: %w", id, ErrLicenseNotFound) + return License{}, fmt.Errorf("license %s: %w", id, ErrLicenseNotFound) } slog.Debug("failed to fetch license by ID", "licenseID", id, "error", err) @@ -246,7 +246,7 @@ func (m *manager) ClaimLicense(ctx context.Context, fingerprint string) (*Licens if err == nil { if !m.config.ExtendOnHeartbeat { // if heartbeat is disabled, we can't extend the claimed license - slog.Warn("license claim conflict due to heartbeat disabled", "nodeID", node.ID, "Fingerprint", node.Fingerprint) + slog.Warn("failed to claim license due to conflict due to heartbeat disabled", "nodeID", node.ID, "Fingerprint", node.Fingerprint) return &LicenseOperationResult{Status: OperationStatusConflict}, nil } diff --git a/internal/licenses/manager_test.go b/internal/licenses/manager_test.go index ca2f7d3..c7cb04a 100644 --- a/internal/licenses/manager_test.go +++ b/internal/licenses/manager_test.go @@ -110,20 +110,20 @@ func TestRemoveLicense_Success(t *testing.T) { // add a license that to be deleted err := manager.AddLicense(ctx, "test_license.lic", "test_key", "test_public_key") - assert.NoError(t, err, "Failed to add license") + assert.NoError(t, err, "failed to add license") // check that the license was created _, err = manager.GetLicenseByID(ctx, "license_test_key") - assert.NoError(t, err, "License should exist") + assert.NoError(t, err, "license should exist") // remove the license err = manager.RemoveLicense(ctx, "license_test_key") - assert.NoError(t, err, "Failed to remove license") + assert.NoError(t, err, "failed to remove license") // check that the license is removed _, err = manager.GetLicenseByID(context.Background(), "license_test_key") - assert.Error(t, err, "License should not exist after deletion") - assert.Contains(t, err.Error(), "license with ID license_test_key: license not found") + assert.Error(t, err, "license should not exist after deletion") + assert.Contains(t, err.Error(), "license license_test_key: license not found") } func TestRemoveLicense_Failure(t *testing.T) { @@ -146,7 +146,7 @@ func TestRemoveLicense_Failure(t *testing.T) { err := manager.RemoveLicense(context.Background(), "invalid_id") assert.Error(t, err) - assert.Contains(t, err.Error(), "license with ID invalid_id not found") + assert.Contains(t, err.Error(), "license invalid_id not found") } func TestListLicenses_Success(t *testing.T) { @@ -228,7 +228,7 @@ func TestGetLicenseByID_Failure(t *testing.T) { _, err := manager.GetLicenseByID(context.Background(), "invalid_id") assert.Error(t, err) - assert.Contains(t, err.Error(), "license with ID invalid_id: license not found") + assert.Contains(t, err.Error(), "license invalid_id: license not found") } func TestClaimLicense_Success(t *testing.T) { diff --git a/internal/output/output.go b/internal/output/output.go index 4528788..53c96cd 100644 --- a/internal/output/output.go +++ b/internal/output/output.go @@ -9,7 +9,7 @@ import ( func PrintError(w io.Writer, message string) { errorMessage := color.New(color.FgRed, color.Bold).SprintFunc() - fmt.Fprintf(w, "%s: %s\n", errorMessage("Error"), message) + fmt.Fprintf(w, "%s: %s\n", errorMessage("error"), message) } func PrintSuccess(w io.Writer, message string) { diff --git a/internal/server/handler.go b/internal/server/handler.go index 391b9d7..9286d0f 100644 --- a/internal/server/handler.go +++ b/internal/server/handler.go @@ -48,10 +48,10 @@ func (h *handler) ClaimLicense(w http.ResponseWriter, r *http.Request) { result, err := h.licenseManager.ClaimLicense(r.Context(), fingerprint) if err != nil { - slog.Error("Failed to claim license", "error", err) + slog.Error("failed to claim license", "error", err) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusInternalServerError) - _ = json.NewEncoder(w).Encode(map[string]string{"error": "Failed to claim license"}) + _ = json.NewEncoder(w).Encode(map[string]string{"error": "failed to claim license"}) return } @@ -71,15 +71,15 @@ func (h *handler) ClaimLicense(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusAccepted) case licenses.OperationStatusConflict: w.WriteHeader(http.StatusConflict) - _ = json.NewEncoder(w).Encode(map[string]string{"error": "License claim conflict, heartbeat disabled"}) + _ = json.NewEncoder(w).Encode(map[string]string{"error": "failed to claim license due to conflict"}) return case licenses.OperationStatusNoLicensesAvailable: w.WriteHeader(http.StatusGone) - _ = json.NewEncoder(w).Encode(map[string]string{"error": "No licenses available"}) + _ = json.NewEncoder(w).Encode(map[string]string{"error": "no licenses available"}) return default: w.WriteHeader(http.StatusInternalServerError) - _ = json.NewEncoder(w).Encode(map[string]string{"error": "Unknown claim status"}) + _ = json.NewEncoder(w).Encode(map[string]string{"error": "unknown claim status"}) return } } @@ -89,10 +89,10 @@ func (h *handler) ReleaseLicense(w http.ResponseWriter, r *http.Request) { result, err := h.licenseManager.ReleaseLicense(r.Context(), fingerprint) if err != nil { - slog.Error("Failed to release license", "error", err) + slog.Error("failed to release license", "error", err) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusInternalServerError) - _ = json.NewEncoder(w).Encode(map[string]string{"error": "Failed to release license"}) + _ = json.NewEncoder(w).Encode(map[string]string{"error": "failed to release license"}) return } @@ -103,9 +103,9 @@ func (h *handler) ReleaseLicense(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNoContent) case licenses.OperationStatusNotFound: w.WriteHeader(http.StatusNotFound) - _ = json.NewEncoder(w).Encode(map[string]string{"error": "Claim not found"}) + _ = json.NewEncoder(w).Encode(map[string]string{"error": "claim not found"}) default: w.WriteHeader(http.StatusInternalServerError) - _ = json.NewEncoder(w).Encode(map[string]string{"error": "Unknown release status"}) + _ = json.NewEncoder(w).Encode(map[string]string{"error": "unknown release status"}) } } diff --git a/internal/server/handler_test.go b/internal/server/handler_test.go index 07c13b1..d0275c8 100644 --- a/internal/server/handler_test.go +++ b/internal/server/handler_test.go @@ -4,13 +4,14 @@ import ( "context" "encoding/json" "errors" + "net/http" + "net/http/httptest" + "testing" + "github.com/gorilla/mux" "github.com/keygen-sh/keygen-relay/internal/licenses" "github.com/keygen-sh/keygen-relay/internal/server" "github.com/keygen-sh/keygen-relay/internal/testutils" - "net/http" - "net/http/httptest" - "testing" "github.com/stretchr/testify/assert" ) @@ -91,7 +92,7 @@ func TestClaimLicense_HeartbeatDisabled_Conflict(t *testing.T) { router.ServeHTTP(rr, req) assert.Equal(t, http.StatusConflict, rr.Code) - assert.Contains(t, rr.Body.String(), "License claim conflict, heartbeat disabled") + assert.Contains(t, rr.Body.String(), "failed to claim license due to conflict") } func TestClaimLicense_NoLicensesAvailable(t *testing.T) { @@ -113,7 +114,7 @@ func TestClaimLicense_NoLicensesAvailable(t *testing.T) { router.ServeHTTP(rr, req) assert.Equal(t, http.StatusGone, rr.Code) - assert.Contains(t, rr.Body.String(), "No licenses available") + assert.Contains(t, rr.Body.String(), "no licenses available") } func TestClaimLicense_InternalServerError(t *testing.T) { @@ -133,7 +134,7 @@ func TestClaimLicense_InternalServerError(t *testing.T) { router.ServeHTTP(rr, req) assert.Equal(t, http.StatusInternalServerError, rr.Code) - assert.Contains(t, rr.Body.String(), "Failed to claim license") + assert.Contains(t, rr.Body.String(), "failed to claim license") } func TestReleaseLicense_Success(t *testing.T) { @@ -177,7 +178,7 @@ func TestReleaseLicense_NotFound(t *testing.T) { router.ServeHTTP(rr, req) assert.Equal(t, http.StatusNotFound, rr.Code) - assert.Contains(t, rr.Body.String(), "Claim not found") + assert.Contains(t, rr.Body.String(), "claim not found") } func TestReleaseLicense_InternalServerError(t *testing.T) { @@ -197,5 +198,5 @@ func TestReleaseLicense_InternalServerError(t *testing.T) { router.ServeHTTP(rr, req) assert.Equal(t, http.StatusInternalServerError, rr.Code) - assert.Contains(t, rr.Body.String(), "Failed to release license") + assert.Contains(t, rr.Body.String(), "failed to release license") } diff --git a/internal/server/server.go b/internal/server/server.go index 0f04c44..344026a 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -38,7 +38,7 @@ func (s *server) Run() error { defer cancel() address := fmt.Sprintf(":%d", s.config.ServerPort) - slog.Info("Starting server", "port", s.config.ServerPort) + slog.Info("starting server", "port", s.config.ServerPort) if s.Config().EnabledHeartbeat { go s.startCullRoutine(ctx) @@ -46,12 +46,12 @@ func (s *server) Run() error { err := http.ListenAndServe(address, s.router) if err != nil && !errors.Is(err, http.ErrServerClosed) { - slog.Error("Server failed to start", "error", err) + slog.Error("server failed to start", "error", err) cancel() return err } - slog.Info("Server stopped") + slog.Info("server stopped") return nil } @@ -75,14 +75,14 @@ func (s *server) startCullRoutine(ctx context.Context) { ticker := time.NewTicker(s.config.CullInterval) defer ticker.Stop() - slog.Debug("Starting cull routine for inactive nodes", "ttl", s.config.TTL, "cullInterval", s.config.CullInterval) + slog.Debug("starting cull routine for inactive nodes", "ttl", s.config.TTL, "cullInterval", s.config.CullInterval) for { select { case <-ticker.C: s.cull() case <-ctx.Done(): - slog.Debug("Stopping cull routine") + slog.Debug("stopping cull routine") return } } @@ -92,8 +92,8 @@ func (s *server) cull() { ctx := context.Background() err := s.manager.CullInactiveNodes(ctx, s.Config().TTL) if err != nil { - slog.Error("Failed to cull inactive nodes", "error", err) + slog.Error("failed to cull inactive nodes", "error", err) } else { - slog.Debug("Successfully culled inactive nodes") + slog.Debug("successfully culled inactive nodes") } } diff --git a/internal/testutils/memory_store.go b/internal/testutils/memory_store.go index 5db69e5..3e2255b 100644 --- a/internal/testutils/memory_store.go +++ b/internal/testutils/memory_store.go @@ -2,36 +2,36 @@ package testutils import ( "database/sql" - schema "github.com/keygen-sh/keygen-relay/db" "log" "testing" + schema "github.com/keygen-sh/keygen-relay/db" "github.com/keygen-sh/keygen-relay/internal/db" _ "github.com/mattn/go-sqlite3" ) func NewMemoryStore(t *testing.T) (*db.Store, *sql.DB) { - dbConn, err := sql.Open("sqlite3", ":memory:?_pragma=foreign_keys(on)") + conn, err := sql.Open("sqlite3", ":memory:?_pragma=foreign_keys(on)") if err != nil { - t.Fatalf("Failed to open in-memory database: %v", err) + t.Fatalf("failed to open in-memory database: %v", err) } - _, err = dbConn.Exec("PRAGMA foreign_keys = ON") + _, err = conn.Exec("PRAGMA foreign_keys = ON") if err != nil { - log.Fatal("Failed to enable foreign keys:", err) + t.Fatalf("failed to enable foreign keys: %v", err) } - if _, err := dbConn.Exec(schema.SchemaSQL); err != nil { - t.Fatalf("Failed to apply schema: %v", err) + if _, err := conn.Exec(schema.Schema); err != nil { + t.Fatalf("failed to apply schema: %v", err) } - store := db.NewStore(db.New(dbConn), dbConn) + store := db.NewStore(db.New(conn), conn) - return store, dbConn + return store, conn } func CloseMemoryStore(dbConn *sql.DB) { if err := dbConn.Close(); err != nil { - log.Printf("Failed to close in-memory database connection: %v", err) + log.Printf("failed to close in-memory database connection: %v", err) } } diff --git a/testdata/claim_no_licenses_available.txt b/testdata/claim_no_licenses_available.txt deleted file mode 100644 index da13786..0000000 --- a/testdata/claim_no_licenses_available.txt +++ /dev/null @@ -1,16 +0,0 @@ -# Start the server without adding any licenses -env PORT=65006 -exec relay serve --port $PORT &server_process_test& - -# Wait for the server to start -exec sleep 1 - -# Attempt to claim a license when none are available -exec curl -s -o response.txt -w "%{http_code}" -X PUT http://localhost:$PORT/v1/nodes/test_fingerprint - -# Expect a gone response with status code 410 and error message -stdout '410' -exec grep 'No licenses available' response.txt - -# Kill the process (stop the server) -kill server_process_test diff --git a/testdata/release_license_not_found.txt b/testdata/release_license_not_found.txt deleted file mode 100644 index 723f9c6..0000000 --- a/testdata/release_license_not_found.txt +++ /dev/null @@ -1,16 +0,0 @@ -# Start the server -env PORT=65008 -exec relay serve --port $PORT &server_process_test& - -# Wait for the server to start -exec sleep 1 - -# Attempt to release a license that was never claimed -exec curl -s -o response.txt -w "%{http_code}" -X DELETE http://localhost:$PORT/v1/nodes/test_fingerprint - -# Expect a not found response with status code 404 and error message -stdout '404' -exec grep '{"error":"Claim not found"}' response.txt - -# Kill the process (stop the server) -kill server_process_test