diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml
new file mode 100644
index 0000000000..137db6d743
--- /dev/null
+++ b/.github/workflows/cd.yml
@@ -0,0 +1,23 @@
+name: ci
+
+on:
+ push:
+ branches: [main]
+
+jobs:
+ deploy:
+ name: Deploy
+ runs-on: ubuntu-latest
+ timeout-minutes: 30
+
+ steps:
+ - name: Check out code
+ uses: actions/checkout@v4
+
+ - name: Set up Go
+ uses: actions/setup-go@v5
+ with:
+ go-version: "1.23.0"
+
+ - name: Build prod
+ run: buildprod.sh
\ No newline at end of file
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000000..3b1fd88eb0
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,50 @@
+name: ci
+
+on:
+ pull_request:
+ branches: [main]
+
+jobs:
+ tests:
+ name: Tests
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Check out code
+ uses: actions/checkout@v4
+
+ - name: Set up Go
+ uses: actions/setup-go@v5
+ with:
+ go-version: "1.23.0"
+
+ - name: Install gosec
+ run: go install github.com/securego/gosec/v2/cmd/gosec@latest
+
+ - name: GO Tests
+ run: go test ./... -cover
+
+ - name: Run Gosec
+ run: gosec ./...
+
+ styles:
+ name: Style
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Check out code
+ uses: actions/checkout@v4
+
+ - name: Set up Go
+ uses: actions/setup-go@v5
+ with:
+ go-version: "1.23.0"
+
+ - name: Indentation
+ run: go fmt ./...
+
+ - name: Install staticcheck
+ run: go install honnef.co/go/tools/cmd/staticcheck@latest
+
+ - name: Run staticcheck
+ run: staticcheck ./...
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000000..13566b81b0
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/learn-cicd-starter.iml b/.idea/learn-cicd-starter.iml
new file mode 100644
index 0000000000..24643cc374
--- /dev/null
+++ b/.idea/learn-cicd-starter.iml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/material_theme_project_new.xml b/.idea/material_theme_project_new.xml
new file mode 100644
index 0000000000..c833fad5ec
--- /dev/null
+++ b/.idea/material_theme_project_new.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000000..d0adac68cd
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000000..35eb1ddfbb
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
index c2bec0368b..14d8bdcaa2 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,5 @@
+![test coverage badge](https://github.com/mogresta/learn-cicd-starter/actions/workflows/ci.yml/badge.svg)
+
# learn-cicd-starter (Notely)
This repo contains the starter code for the "Notely" application for the "Learn CICD" course on [Boot.dev](https://boot.dev).
@@ -21,3 +23,5 @@ go build -o notely && ./notely
*This starts the server in non-database mode.* It will serve a simple webpage at `http://localhost:8080`.
You do *not* need to set up a database or any interactivity on the webpage yet. Instructions for that will come later in the course!
+
+Mario's version of Boot.dev's Notely app.
\ No newline at end of file
diff --git a/internal/auth/auth_test.go b/internal/auth/auth_test.go
new file mode 100644
index 0000000000..b17f367552
--- /dev/null
+++ b/internal/auth/auth_test.go
@@ -0,0 +1,73 @@
+package auth
+
+import (
+ "errors"
+ "net/http"
+ "testing"
+)
+
+func TestGetAPIKey(t *testing.T) {
+ tests := []struct {
+ name string
+ headers http.Header
+ expectedKey string
+ expectedError error
+ }{
+ {
+ name: "valid API key",
+ headers: http.Header{
+ "Authorization": []string{"ApiKey test-api-key-123"},
+ },
+ expectedKey: "test-api-key-123",
+ expectedError: nil,
+ },
+ {
+ name: "missing authorization header",
+ headers: http.Header{},
+ expectedKey: "",
+ expectedError: ErrNoAuthHeaderIncluded,
+ },
+ {
+ name: "malformed authorization header - wrong format",
+ headers: http.Header{
+ "Authorization": []string{"Bearer test-api-key-123"},
+ },
+ expectedKey: "",
+ expectedError: errors.New("malformed authorization header"),
+ },
+ {
+ name: "malformed authorization header - missing key",
+ headers: http.Header{
+ "Authorization": []string{"ApiKey"},
+ },
+ expectedKey: "",
+ expectedError: errors.New("malformed authorization header"),
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ key, err := GetAPIKey(tt.headers)
+
+ // Check error
+ if tt.expectedError != nil {
+ if err == nil {
+ t.Errorf("expected error %v, got nil", tt.expectedError)
+ return
+ }
+ if err.Error() != tt.expectedError.Error() {
+ t.Errorf("expected error %v, got %v", tt.expectedError, err)
+ return
+ }
+ } else if err != nil {
+ t.Errorf("expected no error, got %v", err)
+ return
+ }
+
+ // Check key
+ if key != tt.expectedKey {
+ t.Errorf("expected key %q, got %q", tt.expectedKey, key)
+ }
+ })
+ }
+}
diff --git a/json.go b/json.go
index e346ef4093..27f996cf87 100644
--- a/json.go
+++ b/json.go
@@ -27,5 +27,8 @@ func respondWithJSON(w http.ResponseWriter, code int, payload interface{}) {
return
}
w.WriteHeader(code)
- w.Write(dat)
+ _, err = w.Write(dat)
+ if err != nil {
+ log.Printf("Error responding: %s", err)
+ }
}
diff --git a/main.go b/main.go
index 19d7366c5f..dcf63482b0 100644
--- a/main.go
+++ b/main.go
@@ -7,6 +7,7 @@ import (
"log"
"net/http"
"os"
+ "time"
"github.com/go-chi/chi"
"github.com/go-chi/cors"
@@ -89,10 +90,11 @@ func main() {
router.Mount("/v1", v1Router)
srv := &http.Server{
- Addr: ":" + port,
- Handler: router,
+ Addr: ":" + port,
+ Handler: router,
+ ReadHeaderTimeout: time.Second * 10,
}
log.Printf("Serving on port: %s\n", port)
log.Fatal(srv.ListenAndServe())
-}
+}
\ No newline at end of file