Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: ci_ContinuousIntegration

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.25.1"

- name: Print Go version and exit successfully.
run: go version

- name: Run Auth test cases
run: go test ./... -cover
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,8 @@ 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!

# Rajendra's vesion of boot.dev's notely application.
# Code coverage badge
![Unit Test Code Coverage](https://github.com/rpm-sconex/bootdevz-learn-cicd-starter/actions/workflows/ci.yml/badge.svg)

98 changes: 98 additions & 0 deletions internal/auth/auth_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package auth

import (
"errors"
"net/http"
"testing"
)

func TestGetAPIKey(t *testing.T) {
// Define a slice of test structures with various input scenarios
tests := []struct {
name string // Name of the test case
headers http.Header // Input HTTP headers
expectedAPIKey string // Expected API Key on success
expectedError error // Expected error on failure
}{
{
name: "Success - Correct ApiKey header",
headers: http.Header{
"Authorization": []string{"ApiKey my-secret-api-key"},
},
expectedAPIKey: "my-secret-api-key",
expectedError: nil,
},
{
name: "Failure - No Authorization header",
headers: http.Header{}, // Empty headers
expectedAPIKey: "",
expectedError: ErrNoAuthHeaderIncluded, // Matches the specific error var
},
{
name: "Failure - Header present but empty value",
headers: http.Header{
"Authorization": []string{""},
},
expectedAPIKey: "",
// This case triggers the malformed error due to the Split logic
expectedError: errors.New("malformed authorization header"),
},
{
name: "Failure - Malformed header - missing space/key part",
headers: http.Header{
"Authorization": []string{"ApiKey"},
},
expectedAPIKey: "",
expectedError: errors.New("malformed authorization header"),
},
{
name: "Failure - Malformed header - only value present",
headers: http.Header{
"Authorization": []string{"only-a-key-no-prefix"},
},
expectedAPIKey: "",
expectedError: errors.New("malformed authorization header"),
},
{
name: "Failure - Wrong authorization scheme (e.g., Bearer)",
headers: http.Header{
"Authorization": []string{"Bearer some-jwt-token"},
},
expectedAPIKey: "",
expectedError: errors.New("malformed authorization header"),
},
{
name: "Failure - Extra spaces in header value (library handles trim implicitly)",
headers: http.Header{
"Authorization": []string{" ApiKey my-key-with-spaces "},
},
// Note: http.Header.Get() trims leading/trailing whitespace automatically.
// The Split function then handles internal spacing appropriately for this structure.
expectedAPIKey: "my-key-with-spaces",
expectedError: nil,
},
}

// Iterate through all defined test cases
for _, tt := range tests {
// Run each case as a subtest
t.Run(tt.name, func(t *testing.T) {
gotAPIKey, gotError := GetAPIKey(tt.headers)

// Check for API Key match
if gotAPIKey != tt.expectedAPIKey {
t.Errorf("GetAPIKey() gotAPIKey = %v, want %v", gotAPIKey, tt.expectedAPIKey)
}

// Check for error match
// Using Error() string comparison is generally acceptable for expected static errors in simple functions,
// but for specific known errors like ErrNoAuthHeaderIncluded, an `errors.Is` check is better practice.
if (gotError != nil && tt.expectedError != nil) && gotError.Error() != tt.expectedError.Error() {
t.Errorf("GetAPIKey() gotError = %v, want %v", gotError, tt.expectedError)
} else if (gotError != nil) != (tt.expectedError != nil) {
// Checks if one is nil and the other is not
t.Errorf("GetAPIKey() gotError presence mismatch: got %v, want %v", gotError, tt.expectedError)
}
})
}
}