diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..cc3b539 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,61 @@ +name: lint + +on: + workflow_dispatch: + push: + branches: + - main + pull_request: + +jobs: + golangci-lint: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: Setup Go environment + uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 + with: + go-version: "stable" + - name: Run golangci-lint + uses: golangci/golangci-lint-action@2226d7cb06a077cd73e56eedd38eecad18e5d837 # v6.5.0 + with: + args: --timeout=30m + + govulncheck: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: Setup Go environment + uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 + with: + go-version: "stable" + - name: Run govulncheck + shell: bash + run: | + go install golang.org/x/vuln/cmd/govulncheck@latest + govulncheck ./... + actionlint: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: Setup Go environment + uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 + with: + go-version: "stable" + - name: Install ShellCheck + env: + SHELLCHECK_VER: v0.11.0 + run: | + baseurl=https://github.com/koalaman/shellcheck/releases/download + curl -fsSLO "${baseurl}/${SHELLCHECK_VER}/shellcheck-${SHELLCHECK_VER}.linux.x86_64.tar.xz" + tar -xf "shellcheck-${SHELLCHECK_VER}.linux.x86_64.tar.xz" + sudo mv "./shellcheck-${SHELLCHECK_VER}/shellcheck" "${{ github.action_path }}/shellcheck" + rm -rf "shellcheck-${SHELLCHECK_VER}" "shellcheck-${SHELLCHECK_VER}.linux.x86_64.tar.xz" + - name: Run Actionlint + shell: bash + run: | + go install github.com/rhysd/actionlint/cmd/actionlint@latest + actionlint diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..2d80424 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,28 @@ +name: test + +on: + workflow_dispatch: + push: + branches: + - main + pull_request: + +jobs: + unit-tests: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: Setup Go environment + uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 + with: + go-version: "stable" + - name: Run Unit Tests + run: go test -v ./server/... + - name: Run E2E Tests + run: | + go build -o fifo ./server + ./fifo >logs 2>&1 & + sleep 1 + trap 'kill $!; cat logs' EXIT + go test -v ./client/... diff --git a/server/fifio.go b/server/fifio.go index 0789f9d..3cd58da 100644 --- a/server/fifio.go +++ b/server/fifio.go @@ -224,11 +224,14 @@ func (m *fifoManager) run(ctx context.Context) { } } -func newFifoManager(db *gorm.DB, clock clock.WithDelayedExecution, log *slog.Logger) *fifoManager { - db.AutoMigrate( +func newFifoManager(db *gorm.DB, clock clock.WithDelayedExecution, log *slog.Logger) (*fifoManager, error) { + if err := db.AutoMigrate( &fifo{}, &ticket{}, - ) + ); err != nil { + log.Error("db migration failed", "err", err) + return nil, fmt.Errorf("db migration failed: %w", err) + } fm := &fifoManager{ log: log, db: db, @@ -239,7 +242,7 @@ func newFifoManager(db *gorm.DB, clock clock.WithDelayedExecution, log *slog.Log pullRate: 5 * time.Minute, } go fm.run(context.Background()) - return fm + return fm, nil } func (m *fifoManager) registerHandlers(mux *http.ServeMux) { @@ -310,7 +313,11 @@ func (m *fifoManager) new(w http.ResponseWriter, r *http.Request) { return } - encode(w, 200, api.FifoNewResponse{UUID: fifo.UUID}) + if err := encode(w, 200, api.FifoNewResponse{UUID: fifo.UUID}); err != nil { + log.Error("encoding response failed", "err", err) + http.Error(w, "encoding response failed", http.StatusInternalServerError) + return + } } func (m *fifoManager) ticket(w http.ResponseWriter, r *http.Request) { @@ -390,7 +397,11 @@ func (m *fifoManager) ticket(w http.ResponseWriter, r *http.Request) { } log.Info("ticket created", "ticket", tick.UUID.String()) - encode(w, 200, api.FifoTicketResponse{TicketID: tick.UUID}) + if err := encode(w, 200, api.FifoTicketResponse{TicketID: tick.UUID}); err != nil { + log.Error("encoding response failed", "err", err) + http.Error(w, "encoding response failed", http.StatusInternalServerError) + return + } } func (m *fifoManager) wait(w http.ResponseWriter, r *http.Request) { diff --git a/server/fifo_test.go b/server/fifo_test.go index 2594ac3..09d2f3c 100644 --- a/server/fifo_test.go +++ b/server/fifo_test.go @@ -53,7 +53,8 @@ func newTestFifoManager(t *testing.T, db *gorm.DB, mock sqlmock.Sqlmock, c clock mock.ExpectExec("CREATE TABLE `tickets`"). WillReturnResult(sqlmock.NewResult(0, 0)) - mgr := newFifoManager(db, c, slog.Default()) + mgr, err := newFifoManager(db, c, slog.Default()) + require.NoError(err) require.NoError(mock.ExpectationsWereMet()) return mgr diff --git a/server/main.go b/server/main.go index f569fe7..5739964 100644 --- a/server/main.go +++ b/server/main.go @@ -27,7 +27,11 @@ func main() { } mux := http.NewServeMux() - fm := newFifoManager(db, clock.RealClock{}, log) + fm, err := newFifoManager(db, clock.RealClock{}, log) + if err != nil { + log.Error("fatal", "err", fmt.Errorf("creating fifo manager: %w", err)) + os.Exit(1) + } fm.registerHandlers(mux) if err := http.ListenAndServe(":8080", mux); err != nil {