Skip to content

Commit

Permalink
feat: start bdd acceptance tests (#38)
Browse files Browse the repository at this point in the history
  • Loading branch information
tonitienda authored Apr 29, 2024
1 parent 88179f6 commit 261442a
Show file tree
Hide file tree
Showing 20 changed files with 669 additions and 137 deletions.
22 changes: 20 additions & 2 deletions .github/workflows/build-run-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,30 @@ jobs:
- uses: actions/checkout@v4
- name: Build Docker image
run: docker build -t ${{ matrix.service }} ${{ matrix.service }}
api:
runs-on: ubuntu-latest
needs: build
strategy:
matrix:
runner: [bdd-go]
backend: [go, js]
db: [inmemory, mongo]
steps:
- uses: actions/checkout@v4
- uses: extractions/setup-just@v2
- uses: KengoTODA/actions-setup-docker-compose@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- run: echo "${{ secrets.DOT_ENV }}" > .env
- name: Run Acceptance - API tests
run: just test-${{ matrix.runner }}-${{ matrix.backend }}-${{ matrix.db }}

e2e:
runs-on: ubuntu-latest
needs: build
strategy:
matrix:
e2e_runner: [cypress]
runner: [cypress]
frontend: [next, golang-htmx]
backend: [go, js]
db: [inmemory, mongo]
Expand All @@ -46,7 +64,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- run: echo "${{ secrets.DOT_ENV }}" > .env
- name: Run E2E tests
run: just test-${{ matrix.e2e_runner }}-${{ matrix.frontend }}-${{ matrix.backend }}-${{ matrix.db }}
run: just test-${{ matrix.runner }}-${{ matrix.frontend }}-${{ matrix.backend }}-${{ matrix.db }}
- name: Upload test screenshots
uses: actions/upload-artifact@v2
with:
Expand Down
10 changes: 9 additions & 1 deletion backend-golang-rest/cmd/auth_middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,15 @@ type Auth interface {
func TokenAuthMiddleware(auth Auth) gin.HandlerFunc {

return func(c *gin.Context) {
tokenString := c.Request.Header.Get("Authorization")[7:] // Skip "Bearer "
authHeader := c.Request.Header.Get("Authorization")

if len(authHeader) <= 7 {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Token was not provided"})
c.Abort()
return
}

tokenString := authHeader[7:]

token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
Expand Down
7 changes: 6 additions & 1 deletion backend-golang-rest/pkg/authentication/auth.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package authentication

import "github.com/google/uuid"
import (
"fmt"

"github.com/google/uuid"
)

type User struct {
ID string
Expand Down Expand Up @@ -30,6 +34,7 @@ func (a Auth) GetUserIdBySub(sub string) (string, bool) {
err := a.db.AddUser(user)

if err != nil {
fmt.Printf("The user cound not be added: %v\n", err)
return "", false
}
}
Expand Down
6 changes: 6 additions & 0 deletions backend-golang-rest/pkg/tasks/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,16 @@ func NewTasksHandler(ds TasksDataSource) *TasksHandler {
}

func (h *TasksHandler) GetTasks(c *gin.Context) error {
fmt.Println("Getting tasks")
userId := c.GetString("userId")

fmt.Println("UserId:", userId)

tasks, err := h.datasource.GetTasks(userId)

if err != nil {
fmt.Printf("Error getting tasks: %v\n", err)

return err
}

Expand Down
5 changes: 5 additions & 0 deletions backend-js-rest/src/tasks/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ function makeTaskRouter(datasource) {
}

router.get("/", async (req, res) => {
if (!req.headers["authorization"]) {
res.status(401).end();
return;
}

const tasks = await datasource.getTasks(ownerId);
res.json(tasks);
});
Expand Down
7 changes: 7 additions & 0 deletions bdd-go/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
FROM golang:1.22.1-alpine3.19

WORKDIR /app

COPY . .

CMD ["go", "test", "-v"]
3 changes: 3 additions & 0 deletions bdd-go/acceptance.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package main

func main() {}
46 changes: 46 additions & 0 deletions bdd-go/acceptance_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package main

import (
"testing"

"github.com/cucumber/godog"
"github.com/tonitienda/kadai/acceptance-go/pkg/api"
)

type TestsImpl interface {
UserNotLoggedIn(user string) error
UserIsLoggedIn(user string) error
UserRequestsListOfTasks(user string) error
TheListOfTasksShouldBeEmpty() error
UnauthorizedErrorReturned() error
SuccessfulRequestReturned() error
}

func InitializeScenario(ctx *godog.ScenarioContext) {
InitializeScenario2(ctx, api.NewApiTests())
}

func InitializeScenario2(ctx *godog.ScenarioContext, impl TestsImpl) {

ctx.Given(`^(.*) is not logged in$`, impl.UserNotLoggedIn)
ctx.Given(`^(.*) is logged in$`, impl.UserIsLoggedIn)
ctx.When(`^(.*) requests the list of tasks$`, impl.UserRequestsListOfTasks)
ctx.Then(`^the list of tasks should be empty$`, impl.TheListOfTasksShouldBeEmpty)
ctx.Then(`^the request should be refused because the user is unauthorized$`, impl.UnauthorizedErrorReturned)
ctx.Then(`^the request should be successful$`, impl.SuccessfulRequestReturned)
}

func TestFeatures(t *testing.T) {
suite := godog.TestSuite{
ScenarioInitializer: InitializeScenario,
Options: &godog.Options{
Format: "pretty",
Paths: []string{"features"},
TestingT: t, // Testing instance that will run subtests.
},
}

if suite.Run() != 0 {
t.Fatal("non-zero status returned, failed to run feature tests")
}
}
15 changes: 15 additions & 0 deletions bdd-go/features/permission_tasks.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
Feature: Access the list of tasks

Scenario: Access without login should return a permission error
Given Alice is not logged in
When Alice requests the list of tasks
Then the list of tasks should be empty
And the request should be refused because the user is unauthorized



Scenario: Access after login in should return a successful response
Given Alice is logged in
When Alice requests the list of tasks
Then the list of tasks should be empty
And the request should be successful
15 changes: 15 additions & 0 deletions bdd-go/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module github.com/tonitienda/kadai/acceptance-go

go 1.22.1

require (
github.com/cucumber/gherkin/go/v26 v26.2.0 // indirect
github.com/cucumber/godog v0.14.0 // indirect
github.com/cucumber/messages/go/v21 v21.0.1 // indirect
github.com/gofrs/uuid v4.3.1+incompatible // indirect
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
github.com/hashicorp/go-memdb v1.3.4 // indirect
github.com/hashicorp/golang-lru v0.5.4 // indirect
github.com/spf13/pflag v1.0.5 // indirect
golang.org/x/oauth2 v0.19.0 // indirect
)
45 changes: 45 additions & 0 deletions bdd-go/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cucumber/gherkin/go/v26 v26.2.0 h1:EgIjePLWiPeslwIWmNQ3XHcypPsWAHoMCz/YEBKP4GI=
github.com/cucumber/gherkin/go/v26 v26.2.0/go.mod h1:t2GAPnB8maCT4lkHL99BDCVNzCh1d7dBhCLt150Nr/0=
github.com/cucumber/godog v0.14.0 h1:h/K4t7XBxsFBF+UJEahNqJ1/2VHVepRXCSq3WWWnehs=
github.com/cucumber/godog v0.14.0/go.mod h1:FX3rzIDybWABU4kuIXLZ/qtqEe1Ac5RdXmqvACJOces=
github.com/cucumber/messages/go/v21 v21.0.1 h1:wzA0LxwjlWQYZd32VTlAVDTkW6inOFmSM+RuOwHZiMI=
github.com/cucumber/messages/go/v21 v21.0.1/go.mod h1:zheH/2HS9JLVFukdrsPWoPdmUtmYQAQPLk7w5vWsk5s=
github.com/cucumber/messages/go/v22 v22.0.0/go.mod h1:aZipXTKc0JnjCsXrJnuZpWhtay93k7Rn3Dee7iyPJjs=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gofrs/uuid v4.3.1+incompatible h1:0/KbAdpx3UXAx1kEOWHJeOkpbgRFGHVgv+CFIY7dBJI=
github.com/gofrs/uuid v4.3.1+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/hashicorp/go-immutable-radix v1.3.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc=
github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-memdb v1.3.4 h1:XSL3NR682X/cVk2IeV0d70N4DZ9ljI885xAEU8IoK3c=
github.com/hashicorp/go-memdb v1.3.4/go.mod h1:uBTr1oQbtuMgd1SSGoR8YV27eT3sBHbYiNm53bMpgSg=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
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/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
golang.org/x/oauth2 v0.19.0 h1:9+E/EZBCbTLNrbN35fHv/a/d/mOBatymz1zbtQrXpIg=
golang.org/x/oauth2 v0.19.0/go.mod h1:vYi7skDa1x015PmRRYZ7+s1cWyPgrPiSYRe4rnsexc8=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
95 changes: 95 additions & 0 deletions bdd-go/pkg/api/index.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package api

import (
"fmt"
"net/http"
"os"
)

type Task struct {
ID string `json:"id"`
Title string `json:"title"`
}

type ApiTests struct {
UserTokens map[string]string
UserTasks map[string][]Task
LastRequestedTasks []Task
LastRequestStatus int
BackendUrl string
}

func NewApiTests() *ApiTests {
return &ApiTests{
UserTokens: map[string]string{},
LastRequestedTasks: []Task{},
BackendUrl: os.Getenv("BACKEND_BASE_URL"),
}
}

func (a *ApiTests) UserNotLoggedIn(user string) error {
delete(a.UserTokens, user)
return nil
}

func (a *ApiTests) UserIsLoggedIn(user string) error {
fmt.Println(user, " logged in")
token, err := userLogin(user)

if err != nil {
return err
}

a.UserTokens[user] = token
return nil
}

func (a *ApiTests) UserRequestsListOfTasks(user string) error {
url := fmt.Sprintf("%s/v0/tasks", a.BackendUrl)

client := &http.Client{}
req, _ := http.NewRequest("GET", url, nil)

token, isUserLoggedIn := a.UserTokens[user]

fmt.Printf("User tokens: %v", a.UserTokens)
if isUserLoggedIn {
fmt.Println("User", user, "is logged in")
req.Header.Add("Authorization", "Bearer "+token)
}

res, err := client.Do(req)

if err != nil {
return err
}

a.LastRequestStatus = res.StatusCode

if res.StatusCode == http.StatusOK {
// Deserialize tasks
fmt.Println("correct")
}
return nil
}
func (a *ApiTests) TheListOfTasksShouldBeEmpty() error {
if len(a.LastRequestedTasks) != 0 {
return fmt.Errorf("the list of tasks should be empty")
}
return nil
}
func (a *ApiTests) UnauthorizedErrorReturned() error {
if a.LastRequestStatus != http.StatusUnauthorized {
return fmt.Errorf("expected status %d but %d was returned", http.StatusUnauthorized, a.LastRequestStatus)
}

return nil
}

func (a *ApiTests) SuccessfulRequestReturned() error {
if a.LastRequestStatus != http.StatusOK {
return fmt.Errorf("expected status %d but %d was returned", http.StatusOK, a.LastRequestStatus)
}

return nil
}
46 changes: 46 additions & 0 deletions bdd-go/pkg/api/login.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package api

import (
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"strings"
)

func userLogin(user string) (string, error) {

url := os.Getenv("AUTH0_ISSUER_BASE_URL") + "/oauth/token"

payload := strings.NewReader("{\"client_id\":\"" + os.Getenv("AUTH0_KADAI_BACKEND_CLIENT_ID") + "\",\"client_secret\":\"" + os.Getenv("AUTH0_KADAI_BACKEND_CLIENT_SECRET") + "\",\"audience\":\"" + os.Getenv("AUTH0_AUDIENCE") + "\",\"grant_type\":\"client_credentials\"}")

req, _ := http.NewRequest("POST", url, payload)

req.Header.Add("content-type", "application/json")

res, _ := http.DefaultClient.Do(req)

defer res.Body.Close()
body, _ := io.ReadAll(res.Body)

fmt.Println(res)
fmt.Println(string(body))

var data map[string]interface{}

err := json.Unmarshal(body, &data)
if err != nil {
return "", err
}

fmt.Printf("OK: data=%#v\n", data)

token, ok := data["access_token"]

if !ok {
return "", fmt.Errorf("the token could not be obtained")
}

return fmt.Sprint(token), nil
}
Loading

0 comments on commit 261442a

Please sign in to comment.