Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Writing tests for all server modules #21

Merged
merged 10 commits into from
Dec 6, 2024
29 changes: 29 additions & 0 deletions .github/workflows/go-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Go Test

on:
pull_request:
branches: [main]

jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
directory: [ 'server' ]

steps:
- uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: 1.22
cache-dependency-path: "**/*.sum"
- name: Install dependencies
run: cd ${{ matrix.directory }} && go mod download
- name: Test with Go
run: cd ${{ matrix.directory }} && go test ./... -json > TestResults-${{ matrix.directory }}.json
- name: Upload Go test results
uses: actions/upload-artifact@v4
with:
name: Go-results-${{ matrix.directory }}
path: ./${{ matrix.directory }}/TestResults-${{ matrix.directory }}.json
3 changes: 2 additions & 1 deletion clients/shared_library/interfaces/course_phase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ export interface CoursePhase {
id: string
course_id: string
name: string
meta_data: Array<JSON>
//meta_data: Array<JSON> This DTO is used for getting all courses
// in all courses we do not send the phase meta data
is_initial_phase: boolean
sequence_order: number
course_phase_type_id: string
Expand Down
1 change: 1 addition & 0 deletions server/application/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package application
3 changes: 2 additions & 1 deletion server/course/courseParticipation/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ import (
func setupCourseParticipationRouter(router *gin.RouterGroup) {
// incoming path should be /course/:uuid/
courseParticipation := router.Group("/courses/:uuid/participations")
courseParticipation.GET("/", getCourseParticipationsForCourse)
courseParticipation.GET("", getCourseParticipationsForCourse)
courseParticipation.POST("/enroll", createCourseParticipation)
}

// TODO: in future think about how to integrate / create "passed" students from previous phases
func getCourseParticipationsForCourse(c *gin.Context) {
id, err := uuid.Parse(c.Param("uuid"))
if err != nil {
Expand Down
112 changes: 112 additions & 0 deletions server/course/courseParticipation/router_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package courseParticipation

import (
"bytes"
"context"
"encoding/json"
"log"
"net/http"
"net/http/httptest"
"testing"

"github.com/gin-gonic/gin"
"github.com/google/uuid"
"github.com/niclasheun/prompt2.0/course/courseParticipation/courseParticipationDTO"
"github.com/niclasheun/prompt2.0/testutils"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
)

type RouterTestSuite struct {
suite.Suite
router *gin.Engine
ctx context.Context
cleanup func()
courseParticipationService CourseParticipationService
}

func (suite *RouterTestSuite) SetupSuite() {
suite.ctx = context.Background()

// Set up PostgreSQL container
testDB, cleanup, err := testutils.SetupTestDB(suite.ctx, "../../database_dumps/course_participation_test.sql")
if err != nil {
log.Fatalf("Failed to set up test database: %v", err)
}

suite.cleanup = cleanup
suite.courseParticipationService = CourseParticipationService{
queries: *testDB.Queries,
conn: testDB.Conn,
}
CourseParticipationServiceSingleton = &suite.courseParticipationService

suite.router = setupRouter()
}

func (suite *RouterTestSuite) TearDownSuite() {
suite.cleanup()
}

func setupRouter() *gin.Engine {
router := gin.Default()
api := router.Group("/api")
setupCourseParticipationRouter(api)
return router
}

func (suite *RouterTestSuite) TestGetCourseParticipationsForCourse() {
courseID := "3f42d322-e5bf-4faa-b576-51f2cab14c2e"

req := httptest.NewRequest(http.MethodGet, "/api/courses/"+courseID+"/participations", nil)
w := httptest.NewRecorder()

suite.router.ServeHTTP(w, req)

assert.Equal(suite.T(), http.StatusOK, w.Code)

var participations []courseParticipationDTO.GetCourseParticipation
err := json.Unmarshal(w.Body.Bytes(), &participations)
assert.NoError(suite.T(), err)
assert.Greater(suite.T(), len(participations), 0, "Expected participations for the course")

expectedStudentIDs := []uuid.UUID{
uuid.MustParse("3d1f3b00-87f3-433b-a713-178c4050411a"),
uuid.MustParse("7dc1c4e8-4255-4874-80a0-0c12b958744b"),
uuid.MustParse("500db7ed-2eb2-42d0-82b3-8750e12afa8b"),
}

for i, participation := range participations {
assert.Equal(suite.T(), uuid.MustParse(courseID), participation.CourseID, "Expected CourseID to match")
assert.Equal(suite.T(), expectedStudentIDs[i], participation.StudentID, "Expected StudentID to match")
}
}

func (suite *RouterTestSuite) TestCreateCourseParticipation() {
courseID := "918977e1-2d27-4b55-9064-8504ff027a1a"
newParticipation := courseParticipationDTO.CreateCourseParticipation{
StudentID: uuid.MustParse("3d1f3b00-87f3-433b-a713-178c4050411a"),
}

body, _ := json.Marshal(newParticipation)
req := httptest.NewRequest(http.MethodPost, "/api/courses/"+courseID+"/participations/enroll", bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")
w := httptest.NewRecorder()

suite.router.ServeHTTP(w, req)

assert.Equal(suite.T(), http.StatusOK, w.Code)

var createdParticipation courseParticipationDTO.GetCourseParticipation
err := json.Unmarshal(w.Body.Bytes(), &createdParticipation)
assert.NoError(suite.T(), err)

// Validate the created participation
assert.Equal(suite.T(), uuid.MustParse(courseID), createdParticipation.CourseID, "Expected CourseID to match")
assert.Equal(suite.T(), newParticipation.StudentID, createdParticipation.StudentID, "Expected StudentID to match")
assert.NotEqual(suite.T(), uuid.Nil, createdParticipation.ID, "Expected a valid UUID for the new participation")
}

func TestRouterTestSuite(t *testing.T) {
suite.Run(t, new(RouterTestSuite))
}
97 changes: 97 additions & 0 deletions server/course/courseParticipation/service_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package courseParticipation

import (
"context"
"testing"

"github.com/google/uuid"
"github.com/niclasheun/prompt2.0/course/courseParticipation/courseParticipationDTO"
"github.com/niclasheun/prompt2.0/testutils"
log "github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
)

type CourseParticipationTestSuite struct {
suite.Suite
ctx context.Context
cleanup func()
courseParticipationService CourseParticipationService
}

func (suite *CourseParticipationTestSuite) SetupSuite() {
suite.ctx = context.Background()

// Set up PostgreSQL container
testDB, cleanup, err := testutils.SetupTestDB(suite.ctx, "../../database_dumps/course_participation_test.sql")
if err != nil {
log.Fatalf("Failed to set up test database: %v", err)
}

suite.cleanup = cleanup
suite.courseParticipationService = CourseParticipationService{
queries: *testDB.Queries,
conn: testDB.Conn,
}
CourseParticipationServiceSingleton = &suite.courseParticipationService
}

func (suite *CourseParticipationTestSuite) TearDownSuite() {
suite.cleanup()
}

func (suite *CourseParticipationTestSuite) TestGetAllCourseParticipationsForCourse() {
courseID := uuid.MustParse("3f42d322-e5bf-4faa-b576-51f2cab14c2e")
participations, err := GetAllCourseParticipationsForCourse(suite.ctx, courseID)

assert.NoError(suite.T(), err)
assert.Greater(suite.T(), len(participations), 0, "Expected participations for the course")

expectedStudentIDs := []uuid.UUID{
uuid.MustParse("3d1f3b00-87f3-433b-a713-178c4050411a"),
uuid.MustParse("7dc1c4e8-4255-4874-80a0-0c12b958744b"),
uuid.MustParse("500db7ed-2eb2-42d0-82b3-8750e12afa8b"),
}

for i, participation := range participations {
assert.Equal(suite.T(), courseID, participation.CourseID, "Expected CourseID to match")
assert.Equal(suite.T(), expectedStudentIDs[i], participation.StudentID, "Expected StudentID to match")
}
}

func (suite *CourseParticipationTestSuite) TestGetAllCourseParticipationsForStudent() {
studentID := uuid.MustParse("7dc1c4e8-4255-4874-80a0-0c12b958744b")
participations, err := GetAllCourseParticipationsForStudent(suite.ctx, studentID)

assert.NoError(suite.T(), err)
assert.Greater(suite.T(), len(participations), 0, "Expected participations for the student")

expectedCourseIDs := []uuid.UUID{
uuid.MustParse("3f42d322-e5bf-4faa-b576-51f2cab14c2e"),
uuid.MustParse("918977e1-2d27-4b55-9064-8504ff027a1a"),
}

for i, participation := range participations {
assert.Equal(suite.T(), studentID, participation.StudentID, "Expected StudentID to match")
assert.Equal(suite.T(), expectedCourseIDs[i], participation.CourseID, "Expected CourseID to match")
}
}

func (suite *CourseParticipationTestSuite) TestCreateCourseParticipation() {
newParticipation := courseParticipationDTO.CreateCourseParticipation{
CourseID: uuid.MustParse("918977e1-2d27-4b55-9064-8504ff027a1a"),
StudentID: uuid.MustParse("3d1f3b00-87f3-433b-a713-178c4050411a"),
}

createdParticipation, err := CreateCourseParticipation(suite.ctx, newParticipation)
assert.NoError(suite.T(), err)

// Verify the created participation
assert.Equal(suite.T(), newParticipation.CourseID, createdParticipation.CourseID, "Expected CourseID to match")
assert.Equal(suite.T(), newParticipation.StudentID, createdParticipation.StudentID, "Expected StudentID to match")
assert.NotEqual(suite.T(), uuid.Nil, createdParticipation.ID, "Expected a valid UUID for the new participation")
}

func TestCourseParticipationTestSuite(t *testing.T) {
suite.Run(t, new(CourseParticipationTestSuite))
}
62 changes: 62 additions & 0 deletions server/course/courseParticipation/validation_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package courseParticipation

import (
"testing"

"github.com/google/uuid"
"github.com/niclasheun/prompt2.0/course/courseParticipation/courseParticipationDTO"
"github.com/stretchr/testify/assert"
)

func TestValidate(t *testing.T) {
tests := []struct {
name string
input courseParticipationDTO.CreateCourseParticipation
expectedError string
}{
{
name: "valid course participation",
input: courseParticipationDTO.CreateCourseParticipation{
CourseID: uuid.New(),
StudentID: uuid.New(),
},
expectedError: "",
},
{
name: "missing course ID",
input: courseParticipationDTO.CreateCourseParticipation{
CourseID: uuid.Nil,
StudentID: uuid.New(),
},
expectedError: "validation error: course id is required",
},
{
name: "missing student ID",
input: courseParticipationDTO.CreateCourseParticipation{
CourseID: uuid.New(),
StudentID: uuid.Nil,
},
expectedError: "validation error: student id is required",
},
{
name: "missing both course ID and student ID",
input: courseParticipationDTO.CreateCourseParticipation{
CourseID: uuid.Nil,
StudentID: uuid.Nil,
},
expectedError: "validation error: course id is required",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := Validate(tt.input)
if tt.expectedError == "" {
assert.NoError(t, err)
} else {
assert.Error(t, err)
assert.EqualError(t, err, tt.expectedError)
}
})
}
}
Loading
Loading