Skip to content

Commit

Permalink
Merge pull request #74 from KonferCA/28-be-create-tests-for-all-api-c…
Browse files Browse the repository at this point in the history
…alls

Add tests
  • Loading branch information
juancwu authored Nov 22, 2024
2 parents 6afef5a + 7c35a1a commit 317bc72
Show file tree
Hide file tree
Showing 6 changed files with 679 additions and 1 deletion.
98 changes: 98 additions & 0 deletions internal/server/healthcheck_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package server

import (
"encoding/json"
"net/http"
"net/http/httptest"
"os"
"testing"

"github.com/labstack/echo/v4"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestGetSystemInfo(t *testing.T) {
info := getSystemInfo()

assert.NotEmpty(t, info.Version)
assert.NotEmpty(t, info.GoVersion)
assert.Greater(t, info.NumGoRoutine, 0)
assert.GreaterOrEqual(t, info.MemoryUsage, 0.0)
}

func TestHealthCheckHandler(t *testing.T) {
// setup test environment
os.Setenv("DB_HOST", "localhost")
os.Setenv("DB_PORT", "5432")
os.Setenv("DB_USER", "postgres")
os.Setenv("DB_PASSWORD", "postgres")
os.Setenv("DB_NAME", "postgres")
os.Setenv("DB_SSLMODE", "disable")

// create test server
s, err := New(true)
require.NoError(t, err)
defer s.DBPool.Close()

tests := []struct {
name string
setupFunc func(*Server)
expectedStatus int
expectedHealth string
}{
{
name: "healthy_system",
setupFunc: nil, // no special setup needed
expectedStatus: http.StatusOK,
expectedHealth: "healthy",
},
{
name: "unhealthy_system",
setupFunc: func(s *Server) {
s.DBPool.Close()
},
expectedStatus: http.StatusServiceUnavailable,
expectedHealth: "unhealthy",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// setup
e := echo.New()
req := httptest.NewRequest(http.MethodGet, "/health", nil)
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)

if tt.setupFunc != nil {
tt.setupFunc(s)
}

// test
err := s.handleHealthCheck(c)
require.NoError(t, err)

// sssertions
assert.Equal(t, tt.expectedStatus, rec.Code)

var response HealthReport
err = json.Unmarshal(rec.Body.Bytes(), &response)
require.NoError(t, err)

assert.Equal(t, tt.expectedHealth, response.Status)
assert.NotEmpty(t, response.Timestamp)
assert.NotNil(t, response.System)
assert.NotNil(t, response.Database)

if tt.expectedHealth == "unhealthy" {
assert.False(t, response.Database.Connected)
assert.NotEmpty(t, response.Database.Error)
} else {
assert.True(t, response.Database.Connected)
assert.NotEmpty(t, response.Database.PostgresVersion)
assert.GreaterOrEqual(t, response.Database.LatencyMs, 0.0)
}
})
}
}
230 changes: 230 additions & 0 deletions internal/server/project_comment_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
package server

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

"github.com/google/uuid"
"github.com/stretchr/testify/assert"
)

func TestProjectCommentEndpoints(t *testing.T) {
// setup test environment
os.Setenv("DB_HOST", "localhost")
os.Setenv("DB_PORT", "5432")
os.Setenv("DB_USER", "postgres")
os.Setenv("DB_PASSWORD", "postgres")
os.Setenv("DB_NAME", "postgres")
os.Setenv("DB_SSLMODE", "disable")

// create server
s, err := New(true)
if err != nil {
t.Fatalf("failed to create server: %v", err)
}
defer s.DBPool.Close()

// clean up database before tests
ctx := context.Background()
_, err = s.DBPool.Exec(ctx, "DELETE FROM project_comments")
if err != nil {
t.Fatalf("failed to clean up project_comments: %v", err)
}
_, err = s.DBPool.Exec(ctx, "DELETE FROM projects")
if err != nil {
t.Fatalf("failed to clean up projects: %v", err)
}
_, err = s.DBPool.Exec(ctx, "DELETE FROM companies WHERE name = $1", "Test Company")
if err != nil {
t.Fatalf("failed to clean up companies: %v", err)
}
_, err = s.DBPool.Exec(ctx, "DELETE FROM users WHERE email = $1", "[email protected]")
if err != nil {
t.Fatalf("failed to clean up test user: %v", err)
}

// Create a test user directly in the database
userID := uuid.New().String()
_, err = s.DBPool.Exec(ctx, `
INSERT INTO users (id, email, password_hash, first_name, last_name, role)
VALUES ($1, $2, $3, $4, $5, 'startup_owner')
`, userID, "[email protected]", "hashedpassword", "Test", "User")
if err != nil {
t.Fatalf("failed to create test user: %v", err)
}

// Create a company
description := "Test Company Description"
companyPayload := CreateCompanyRequest{
OwnerUserID: userID,
Name: "Test Company",
Description: &description,
}
companyBody, _ := json.Marshal(companyPayload)

req := httptest.NewRequest(http.MethodPost, "/api/v1/companies", bytes.NewReader(companyBody))
req.Header.Set("Content-Type", "application/json")
rec := httptest.NewRecorder()
s.echoInstance.ServeHTTP(rec, req)

t.Logf("Company creation response: %s", rec.Body.String())

var companyResponse map[string]interface{}
err = json.NewDecoder(rec.Body).Decode(&companyResponse)
if !assert.NoError(t, err) {
t.Fatalf("Failed to decode company response: %v", err)
}

companyID, ok := companyResponse["ID"].(string)
if !assert.True(t, ok, "Company ID should be a string") {
t.Fatalf("Failed to get company ID from response: %v", companyResponse)
}

// Create a project
projectDescription := "Test Description"
projectPayload := CreateProjectRequest{
CompanyID: companyID,
Title: "Test Project",
Description: &projectDescription,
Status: "draft",
}
projectBody, _ := json.Marshal(projectPayload)

req = httptest.NewRequest(http.MethodPost, "/api/v1/projects", bytes.NewReader(projectBody))
req.Header.Set("Content-Type", "application/json")
rec = httptest.NewRecorder()
s.echoInstance.ServeHTTP(rec, req)

t.Logf("Project creation response: %s", rec.Body.String())

var projectResponse map[string]interface{}
err = json.NewDecoder(rec.Body).Decode(&projectResponse)
if !assert.NoError(t, err) {
t.Fatalf("Failed to decode project response: %v", err)
}

projectID, ok := projectResponse["ID"].(string)
if !assert.True(t, ok, "Project ID should be a string") {
t.Fatalf("Failed to get project ID from response: %v", projectResponse)
}

// test create comment
t.Run("create comment", func(t *testing.T) {
commentPayload := CreateProjectCommentRequest{
UserID: userID,
Comment: "This is a test comment",
}
body, _ := json.Marshal(commentPayload)

req := httptest.NewRequest(http.MethodPost, "/api/v1/projects/"+projectID+"/comments", bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")
rec := httptest.NewRecorder()

s.echoInstance.ServeHTTP(rec, req)
t.Logf("Create comment response: %s", rec.Body.String())
assert.Equal(t, http.StatusCreated, rec.Code)

var response map[string]interface{}
err := json.NewDecoder(rec.Body).Decode(&response)
assert.NoError(t, err)
assert.Equal(t, projectID, response["ProjectID"])
assert.Equal(t, commentPayload.Comment, response["Comment"])
})

// test list comments
t.Run("list comments", func(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/api/v1/projects/"+projectID+"/comments", nil)
rec := httptest.NewRecorder()

s.echoInstance.ServeHTTP(rec, req)
t.Logf("List comments response: %s", rec.Body.String())
assert.Equal(t, http.StatusOK, rec.Code)

var response []map[string]interface{}
err := json.NewDecoder(rec.Body).Decode(&response)
assert.NoError(t, err)
assert.Len(t, response, 1)
assert.Equal(t, "This is a test comment", response[0]["Comment"])
})

// test delete comment
t.Run("delete comment", func(t *testing.T) {
// Get the comment ID from the list response
req := httptest.NewRequest(http.MethodGet, "/api/v1/projects/"+projectID+"/comments", nil)
rec := httptest.NewRecorder()
s.echoInstance.ServeHTTP(rec, req)

var listResponse []map[string]interface{}
err := json.NewDecoder(rec.Body).Decode(&listResponse)
assert.NoError(t, err)
assert.NotEmpty(t, listResponse)

commentID := listResponse[0]["ID"].(string)

// Delete the comment
req = httptest.NewRequest(http.MethodDelete, "/api/v1/projects/comments/"+commentID, nil)
rec = httptest.NewRecorder()

s.echoInstance.ServeHTTP(rec, req)
t.Logf("Delete comment response: %s", rec.Body.String())
assert.Equal(t, http.StatusNoContent, rec.Code)

// Verify deletion
req = httptest.NewRequest(http.MethodGet, "/api/v1/projects/"+projectID+"/comments", nil)
rec = httptest.NewRecorder()

s.echoInstance.ServeHTTP(rec, req)
t.Logf("List comments after delete response: %s", rec.Body.String())
assert.Equal(t, http.StatusOK, rec.Code)

var response []map[string]interface{}
err = json.NewDecoder(rec.Body).Decode(&response)
assert.NoError(t, err)
assert.Len(t, response, 0, "Comment list should be empty after deletion")
})

// test error cases
t.Run("create comment with invalid project ID", func(t *testing.T) {
commentPayload := CreateProjectCommentRequest{
UserID: userID,
Comment: "This is a test comment",
}
body, _ := json.Marshal(commentPayload)

req := httptest.NewRequest(http.MethodPost, "/api/v1/projects/invalid-uuid/comments", bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")
rec := httptest.NewRecorder()

s.echoInstance.ServeHTTP(rec, req)
assert.Equal(t, http.StatusBadRequest, rec.Code)
})

t.Run("create comment with invalid user ID", func(t *testing.T) {
commentPayload := CreateProjectCommentRequest{
UserID: "invalid-uuid",
Comment: "This is a test comment",
}
body, _ := json.Marshal(commentPayload)

req := httptest.NewRequest(http.MethodPost, "/api/v1/projects/"+projectID+"/comments", bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")
rec := httptest.NewRecorder()

s.echoInstance.ServeHTTP(rec, req)
assert.Equal(t, http.StatusBadRequest, rec.Code)
})

t.Run("delete non-existent comment", func(t *testing.T) {
nonExistentID := uuid.New().String()
req := httptest.NewRequest(http.MethodDelete, "/api/v1/projects/comments/"+nonExistentID, nil)
rec := httptest.NewRecorder()

s.echoInstance.ServeHTTP(rec, req)
assert.Equal(t, http.StatusNotFound, rec.Code)
})
}
Loading

0 comments on commit 317bc72

Please sign in to comment.