From b2eb9d31165d806f2986430e499aa88c6d807f33 Mon Sep 17 00:00:00 2001 From: despiegk Date: Fri, 23 May 2025 13:44:18 +0400 Subject: [PATCH] ... --- pkg/heroagent/handlers/job_handlers.go | 288 +- pkg/heroagent/handlers/job_handlers_test.go | 630 +-- .../handlers/job_handlers_test.go.bak | 4975 ----------------- pkg/heroagent/pages/jobs.go | 61 +- pkg/herojobs/processjob.go | 2 +- 5 files changed, 476 insertions(+), 5480 deletions(-) delete mode 100644 pkg/heroagent/handlers/job_handlers_test.go.bak diff --git a/pkg/heroagent/handlers/job_handlers.go b/pkg/heroagent/handlers/job_handlers.go index 693fe93..3c740f1 100644 --- a/pkg/heroagent/handlers/job_handlers.go +++ b/pkg/heroagent/handlers/job_handlers.go @@ -3,40 +3,40 @@ package handlers import ( "fmt" "log" + "strconv" // Added strconv for JobID parsing "github.com/freeflowuniverse/heroagent/pkg/herojobs" "github.com/gofiber/fiber/v2" ) -// HeroJobsClientInterface defines the interface for the HeroJobs client -type HeroJobsClientInterface interface { - Connect() error - Close() error - SubmitJob(job *herojobs.Job) (*herojobs.Job, error) - GetJob(jobID string) (*herojobs.Job, error) - DeleteJob(jobID string) error - ListJobs(circleID, topic string) ([]string, error) +// RedisClientInterface defines the methods JobHandler needs from a HeroJobs Redis client. +type RedisClientInterface interface { + StoreJob(job *herojobs.Job) error + EnqueueJob(job *herojobs.Job) error + GetJob(jobID interface{}) (*herojobs.Job, error) // Changed jobID type to interface{} + ListJobs(circleID, topic string) ([]uint32, error) QueueSize(circleID, topic string) (int64, error) QueueEmpty(circleID, topic string) error - QueueGet(circleID, topic string) (*herojobs.Job, error) - CreateJob(circleID, topic, sessionKey, heroScript, rhaiScript string) (*herojobs.Job, error) + // herojobs.Job also has Load() and Save() methods, but those are on the Job object itself, + // not typically part of the client interface unless the client is a facade for all job operations. } // JobHandler handles job-related routes type JobHandler struct { - client HeroJobsClientInterface + client RedisClientInterface // Changed to use the interface logger *log.Logger } // NewJobHandler creates a new JobHandler -func NewJobHandler(socketPath string, logger *log.Logger) (*JobHandler, error) { - client, err := herojobs.NewClient(socketPath) +func NewJobHandler(redisAddr string, logger *log.Logger) (*JobHandler, error) { + redisClient, err := herojobs.NewRedisClient(redisAddr, false) if err != nil { - return nil, fmt.Errorf("failed to create HeroJobs client: %w", err) + return nil, fmt.Errorf("failed to create HeroJobs Redis client: %w", err) } - + // *herojobs.RedisClient must implement RedisClientInterface. + // This assignment is valid if *herojobs.RedisClient has all methods of RedisClientInterface. return &JobHandler{ - client: client, + client: redisClient, logger: logger, }, nil } @@ -76,14 +76,6 @@ func (h *JobHandler) RegisterRoutes(app *fiber.App) { // @Router /api/jobs/submit [post] // @Router /admin/jobs/submit [post] func (h *JobHandler) submitJob(c *fiber.Ctx) error { - // Connect to the HeroJobs server - if err := h.client.Connect(); err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "error": fmt.Sprintf("Failed to connect to HeroJobs server: %v", err), - }) - } - defer h.client.Close() - // Parse job from request body var job herojobs.Job if err := c.BodyParser(&job); err != nil { @@ -92,15 +84,32 @@ func (h *JobHandler) submitJob(c *fiber.Ctx) error { }) } - // Submit job - submittedJob, err := h.client.SubmitJob(&job) - if err != nil { + // Save job to OurDB (this assigns/confirms JobID) + if err := job.Save(); err != nil { + h.logger.Printf("Failed to save job to OurDB: %v", err) return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "error": fmt.Sprintf("Failed to submit job: %v", err), + "error": fmt.Sprintf("Failed to save job: %v", err), }) } - return c.JSON(submittedJob) + // Store job in Redis + if err := h.client.StoreJob(&job); err != nil { + h.logger.Printf("Failed to store job in Redis: %v", err) + // Attempt to roll back or log, but proceed to enqueue if critical + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "error": fmt.Sprintf("Failed to store job in Redis: %v", err), + }) + } + + // Enqueue job in Redis + if err := h.client.EnqueueJob(&job); err != nil { + h.logger.Printf("Failed to enqueue job in Redis: %v", err) + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "error": fmt.Sprintf("Failed to enqueue job: %v", err), + }) + } + + return c.JSON(job) } // @Summary Get a job @@ -114,28 +123,36 @@ func (h *JobHandler) submitJob(c *fiber.Ctx) error { // @Router /api/jobs/get/{id} [get] // @Router /admin/jobs/get/{id} [get] func (h *JobHandler) getJob(c *fiber.Ctx) error { - // Connect to the HeroJobs server - if err := h.client.Connect(); err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "error": fmt.Sprintf("Failed to connect to HeroJobs server: %v", err), - }) - } - defer h.client.Close() - // Get job ID from path parameter - jobID := c.Params("id") - if jobID == "" { + jobIDStr := c.Params("id") + if jobIDStr == "" { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "error": "Job ID is required", }) } - // Get job + // Convert jobID string to uint32 + jobID64, err := strconv.ParseUint(jobIDStr, 10, 32) + if err != nil { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "error": fmt.Sprintf("Invalid Job ID format: %s. %v", jobIDStr, err), + }) + } + jobID := uint32(jobID64) + + // Get job from Redis first job, err := h.client.GetJob(jobID) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "error": fmt.Sprintf("Failed to get job: %v", err), - }) + // If not found in Redis (e.g. redis.Nil or other error), try OurDB + h.logger.Printf("Job %d not found in Redis or error: %v. Trying OurDB.", jobID, err) + retrievedJob := &herojobs.Job{JobID: jobID} + if loadErr := retrievedJob.Load(); loadErr != nil { + h.logger.Printf("Failed to load job %d from OurDB: %v", jobID, loadErr) + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "error": fmt.Sprintf("Failed to get job %d: %v / %v", jobID, err, loadErr), + }) + } + job = retrievedJob // Use the job loaded from OurDB } return c.JSON(job) @@ -152,32 +169,22 @@ func (h *JobHandler) getJob(c *fiber.Ctx) error { // @Router /api/jobs/delete/{id} [delete] // @Router /admin/jobs/delete/{id} [delete] func (h *JobHandler) deleteJob(c *fiber.Ctx) error { - // Connect to the HeroJobs server - if err := h.client.Connect(); err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "error": fmt.Sprintf("Failed to connect to HeroJobs server: %v", err), - }) - } - defer h.client.Close() - // Get job ID from path parameter - jobID := c.Params("id") - if jobID == "" { + jobIDStr := c.Params("id") + if jobIDStr == "" { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "error": "Job ID is required", }) } - // Delete job - if err := h.client.DeleteJob(jobID); err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "error": fmt.Sprintf("Failed to delete job: %v", err), - }) - } - - return c.JSON(fiber.Map{ - "status": "success", - "message": fmt.Sprintf("Job %s deleted successfully", jobID), + // Deleting jobs requires removing from OurDB and Redis. + // This functionality is not directly provided by RedisClient.DeleteJob + // and OurDB job deletion is not specified in README. + // For now, returning not implemented. + h.logger.Printf("Attempt to delete job %s - not implemented", jobIDStr) + return c.Status(fiber.StatusNotImplemented).JSON(fiber.Map{ + "error": "Job deletion is not implemented", + "message": fmt.Sprintf("Job %s deletion requested but not implemented.", jobIDStr), }) } @@ -193,14 +200,6 @@ func (h *JobHandler) deleteJob(c *fiber.Ctx) error { // @Router /api/jobs/list [get] // @Router /admin/jobs/list [get] func (h *JobHandler) listJobs(c *fiber.Ctx) error { - // Connect to the HeroJobs server - if err := h.client.Connect(); err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "error": fmt.Sprintf("Failed to connect to HeroJobs server: %v", err), - }) - } - defer h.client.Close() - // Get parameters from query circleID := c.Query("circleid") if circleID == "" { @@ -242,14 +241,6 @@ func (h *JobHandler) listJobs(c *fiber.Ctx) error { // @Router /api/jobs/queue/size [get] // @Router /admin/jobs/queue/size [get] func (h *JobHandler) queueSize(c *fiber.Ctx) error { - // Connect to the HeroJobs server - if err := h.client.Connect(); err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "error": fmt.Sprintf("Failed to connect to HeroJobs server: %v", err), - }) - } - defer h.client.Close() - // Get parameters from query circleID := c.Query("circleid") if circleID == "" { @@ -291,14 +282,6 @@ func (h *JobHandler) queueSize(c *fiber.Ctx) error { // @Router /api/jobs/queue/empty [post] // @Router /admin/jobs/queue/empty [post] func (h *JobHandler) queueEmpty(c *fiber.Ctx) error { - // Connect to the HeroJobs server - if err := h.client.Connect(); err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "error": fmt.Sprintf("Failed to connect to HeroJobs server: %v", err), - }) - } - defer h.client.Close() - // Parse parameters from request body var params struct { CircleID string `json:"circleid"` @@ -347,14 +330,6 @@ func (h *JobHandler) queueEmpty(c *fiber.Ctx) error { // @Router /api/jobs/queue/get [get] // @Router /admin/jobs/queue/get [get] func (h *JobHandler) queueGet(c *fiber.Ctx) error { - // Connect to the HeroJobs server - if err := h.client.Connect(); err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "error": fmt.Sprintf("Failed to connect to HeroJobs server: %v", err), - }) - } - defer h.client.Close() - // Get parameters from query circleID := c.Query("circleid") if circleID == "" { @@ -370,14 +345,40 @@ func (h *JobHandler) queueGet(c *fiber.Ctx) error { }) } - // Get job from queue - job, err := h.client.QueueGet(circleID, topic) + // Get list of job IDs (uint32) from the queue (non-destructive) + jobIDs, err := h.client.ListJobs(circleID, topic) if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "error": fmt.Sprintf("Failed to get job from queue: %v", err), + "error": fmt.Sprintf("Failed to list jobs in queue: %v", err), }) } + if len(jobIDs) == 0 { + return c.Status(fiber.StatusNotFound).JSON(fiber.Map{ + "error": "Queue is empty or no jobs found", + }) + } + + // Take the first job ID from the list (it's already uint32) + jobIDToFetch := jobIDs[0] + + // Get the actual job details using the ID + job, err := h.client.GetJob(jobIDToFetch) + if err != nil { + // If not found in Redis (e.g. redis.Nil or other error), try OurDB + h.logger.Printf("Job %d (from queue list) not found in Redis or error: %v. Trying OurDB.", jobIDToFetch, err) + retrievedJob := &herojobs.Job{JobID: jobIDToFetch} // Ensure CircleID and Topic are set if Load needs them + retrievedJob.CircleID = circleID // Needed for Load if path depends on it + retrievedJob.Topic = topic // Needed for Load if path depends on it + if loadErr := retrievedJob.Load(); loadErr != nil { + h.logger.Printf("Failed to load job %d from OurDB: %v", jobIDToFetch, loadErr) + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "error": fmt.Sprintf("Failed to get job %d from queue (Redis err: %v / OurDB err: %v)", jobIDToFetch, err, loadErr), + }) + } + job = retrievedJob // Use the job loaded from OurDB + } + return c.JSON(job) } @@ -393,51 +394,92 @@ func (h *JobHandler) queueGet(c *fiber.Ctx) error { // @Router /api/jobs/create [post] // @Router /admin/jobs/create [post] func (h *JobHandler) createJob(c *fiber.Ctx) error { - // Connect to the HeroJobs server - if err := h.client.Connect(); err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "error": fmt.Sprintf("Failed to connect to HeroJobs server: %v", err), - }) - } - defer h.client.Close() - // Parse parameters from request body - var params struct { + var reqBody struct { CircleID string `json:"circleid"` Topic string `json:"topic"` SessionKey string `json:"sessionkey"` - HeroScript string `json:"heroscript"` - RhaiScript string `json:"rhaiscript"` + Params string `json:"params"` + ParamsType string `json:"paramstype"` + Timeout int64 `json:"timeout"` // Optional: allow timeout override + Log bool `json:"log"` // Optional: allow log enabling } - if err := c.BodyParser(¶ms); err != nil { + if err := c.BodyParser(&reqBody); err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "error": fmt.Sprintf("Failed to parse parameters: %v", err), }) } - if params.CircleID == "" { + if reqBody.CircleID == "" { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "error": "Circle ID is required", }) } - - if params.Topic == "" { + if reqBody.Topic == "" { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "error": "Topic is required", }) } + if reqBody.Params == "" { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "error": "Params are required", + }) + } + if reqBody.ParamsType == "" { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "error": "ParamsType is required", + }) + } - // Create job - job, err := h.client.CreateJob( - params.CircleID, - params.Topic, - params.SessionKey, - params.HeroScript, - params.RhaiScript, - ) - if err != nil { + // Create a new job instance + job := herojobs.NewJob() // Initializes with defaults + job.CircleID = reqBody.CircleID + job.Topic = reqBody.Topic + job.SessionKey = reqBody.SessionKey + job.Params = reqBody.Params + + // Convert ParamsType string to herojobs.ParamsType + switch herojobs.ParamsType(reqBody.ParamsType) { + case herojobs.ParamsTypeHeroScript: + job.ParamsType = herojobs.ParamsTypeHeroScript + case herojobs.ParamsTypeRhaiScript: + job.ParamsType = herojobs.ParamsTypeRhaiScript + case herojobs.ParamsTypeOpenRPC: + job.ParamsType = herojobs.ParamsTypeOpenRPC + case herojobs.ParamsTypeAI: + job.ParamsType = herojobs.ParamsTypeAI + default: + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "error": fmt.Sprintf("Invalid ParamsType: %s", reqBody.ParamsType), + }) + } + + if reqBody.Timeout > 0 { + job.Timeout = reqBody.Timeout + } + job.Log = reqBody.Log + + // Save job to OurDB (this assigns JobID) + if err := job.Save(); err != nil { + h.logger.Printf("Failed to save new job to OurDB: %v", err) return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "error": fmt.Sprintf("Failed to create job: %v", err), + "error": fmt.Sprintf("Failed to save new job: %v", err), + }) + } + + // Store job in Redis + if err := h.client.StoreJob(job); err != nil { + h.logger.Printf("Failed to store new job in Redis: %v", err) + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "error": fmt.Sprintf("Failed to store new job in Redis: %v", err), + }) + } + + // Enqueue job in Redis + if err := h.client.EnqueueJob(job); err != nil { + h.logger.Printf("Failed to enqueue new job in Redis: %v", err) + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "error": fmt.Sprintf("Failed to enqueue new job: %v", err), }) } diff --git a/pkg/heroagent/handlers/job_handlers_test.go b/pkg/heroagent/handlers/job_handlers_test.go index 889a7f6..bd3638d 100644 --- a/pkg/heroagent/handlers/job_handlers_test.go +++ b/pkg/heroagent/handlers/job_handlers_test.go @@ -16,34 +16,25 @@ import ( "github.com/stretchr/testify/mock" ) -// MockHeroJobsClient is a mock implementation of the HeroJobs client -type MockHeroJobsClient struct { +// MockRedisClient is a mock implementation of the RedisClientInterface +type MockRedisClient struct { mock.Mock } -// Connect mocks the Connect method -func (m *MockHeroJobsClient) Connect() error { - args := m.Called() - return args.Error(0) -} - -// Close mocks the Close method -func (m *MockHeroJobsClient) Close() error { - args := m.Called() - return args.Error(0) -} - -// SubmitJob mocks the SubmitJob method -func (m *MockHeroJobsClient) SubmitJob(job *herojobs.Job) (*herojobs.Job, error) { +// StoreJob mocks the StoreJob method +func (m *MockRedisClient) StoreJob(job *herojobs.Job) error { args := m.Called(job) - if args.Get(0) == nil { - return nil, args.Error(1) - } - return args.Get(0).(*herojobs.Job), args.Error(1) + return args.Error(0) +} + +// EnqueueJob mocks the EnqueueJob method +func (m *MockRedisClient) EnqueueJob(job *herojobs.Job) error { + args := m.Called(job) + return args.Error(0) } // GetJob mocks the GetJob method -func (m *MockHeroJobsClient) GetJob(jobID string) (*herojobs.Job, error) { +func (m *MockRedisClient) GetJob(jobID interface{}) (*herojobs.Job, error) { // jobID is interface{} args := m.Called(jobID) if args.Get(0) == nil { return nil, args.Error(1) @@ -51,71 +42,54 @@ func (m *MockHeroJobsClient) GetJob(jobID string) (*herojobs.Job, error) { return args.Get(0).(*herojobs.Job), args.Error(1) } -// DeleteJob mocks the DeleteJob method -func (m *MockHeroJobsClient) DeleteJob(jobID string) error { - args := m.Called(jobID) - return args.Error(0) -} - // ListJobs mocks the ListJobs method -func (m *MockHeroJobsClient) ListJobs(circleID, topic string) ([]string, error) { +func (m *MockRedisClient) ListJobs(circleID, topic string) ([]uint32, error) { // Returns []uint32 args := m.Called(circleID, topic) if args.Get(0) == nil { return nil, args.Error(1) } - return args.Get(0).([]string), args.Error(1) + return args.Get(0).([]uint32), args.Error(1) } // QueueSize mocks the QueueSize method -func (m *MockHeroJobsClient) QueueSize(circleID, topic string) (int64, error) { +func (m *MockRedisClient) QueueSize(circleID, topic string) (int64, error) { args := m.Called(circleID, topic) + // Ensure Get(0) is not nil before type assertion if it can be nil in some error cases + if args.Get(0) == nil && args.Error(1) != nil { // If error is set, result might be nil + return 0, args.Error(1) + } return args.Get(0).(int64), args.Error(1) } // QueueEmpty mocks the QueueEmpty method -func (m *MockHeroJobsClient) QueueEmpty(circleID, topic string) error { +func (m *MockRedisClient) QueueEmpty(circleID, topic string) error { args := m.Called(circleID, topic) return args.Error(0) } -// QueueGet mocks the QueueGet method -func (m *MockHeroJobsClient) QueueGet(circleID, topic string) (*herojobs.Job, error) { - args := m.Called(circleID, topic) - if args.Get(0) == nil { - return nil, args.Error(1) - } - return args.Get(0).(*herojobs.Job), args.Error(1) -} - -// CreateJob mocks the CreateJob method -func (m *MockHeroJobsClient) CreateJob(circleID, topic, sessionKey, heroScript, rhaiScript string) (*herojobs.Job, error) { - args := m.Called(circleID, topic, sessionKey, heroScript, rhaiScript) - if args.Get(0) == nil { - return nil, args.Error(1) - } - return args.Get(0).(*herojobs.Job), args.Error(1) -} - // setupTest initializes a test environment with a mock client -func setupTest() (*JobHandler, *MockHeroJobsClient, *fiber.App) { - mockClient := new(MockHeroJobsClient) +func setupTest() (*JobHandler, *MockRedisClient, *fiber.App) { + mockClient := new(MockRedisClient) handler := &JobHandler{ - client: mockClient, + client: mockClient, // Assign the mock that implements RedisClientInterface } app := fiber.New() - // Register routes - api := app.Group("/api") - jobs := api.Group("/jobs") - jobs.Post("/create", handler.createJob) - jobs.Get("/queue/get", handler.queueGet) - jobs.Post("/queue/empty", handler.queueEmpty) - jobs.Post("/submit", handler.submitJob) - jobs.Get("/get/:jobid", handler.getJob) - jobs.Delete("/delete/:jobid", handler.deleteJob) - jobs.Get("/list", handler.listJobs) - jobs.Get("/queue/size", handler.queueSize) + // Register routes (ensure these match the actual routes in job_handlers.go) + apiJobs := app.Group("/api/jobs") // Assuming routes are under /api/jobs + apiJobs.Post("/submit", handler.submitJob) + apiJobs.Get("/get/:id", handler.getJob) // :id as per job_handlers.go + apiJobs.Delete("/delete/:id", handler.deleteJob) // :id as per job_handlers.go + apiJobs.Get("/list", handler.listJobs) + apiJobs.Get("/queue/size", handler.queueSize) + apiJobs.Post("/queue/empty", handler.queueEmpty) + apiJobs.Get("/queue/get", handler.queueGet) + apiJobs.Post("/create", handler.createJob) + + // If admin routes are also tested, they need to be registered here too + // adminJobs := app.Group("/admin/jobs") + // jobRoutes(adminJobs) // if using the same handler instance return handler, mockClient, app } @@ -134,7 +108,6 @@ func TestQueueEmpty(t *testing.T) { name string circleID string topic string - connectError error emptyError error expectedStatus int expectedBody string @@ -143,25 +116,15 @@ func TestQueueEmpty(t *testing.T) { name: "Success", circleID: "test-circle", topic: "test-topic", - connectError: nil, emptyError: nil, expectedStatus: fiber.StatusOK, expectedBody: `{"status":"success","message":"Queue for circle test-circle and topic test-topic emptied successfully"}`, }, - { - name: "Connection Error", - circleID: "test-circle", - topic: "test-topic", - connectError: errors.New("connection error"), - emptyError: nil, - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to connect to HeroJobs server: connection error"}`, - }, + // Removed "Connection Error" test case as Connect is no longer directly called per op { name: "Empty Error", circleID: "test-circle", topic: "test-topic", - connectError: nil, emptyError: errors.New("empty error"), expectedStatus: fiber.StatusInternalServerError, expectedBody: `{"error":"Failed to empty queue: empty error"}`, @@ -170,7 +133,6 @@ func TestQueueEmpty(t *testing.T) { name: "Empty Circle ID", circleID: "", topic: "test-topic", - connectError: nil, emptyError: nil, expectedStatus: fiber.StatusBadRequest, expectedBody: `{"error":"Circle ID is required"}`, @@ -179,41 +141,22 @@ func TestQueueEmpty(t *testing.T) { name: "Empty Topic", circleID: "test-circle", topic: "", - connectError: nil, emptyError: nil, expectedStatus: fiber.StatusBadRequest, expectedBody: `{"error":"Topic is required"}`, }, } - + for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - // Create a new mock client for each test - mockClient := new(MockHeroJobsClient) - - // Setup mock expectations - Connect is always called in the handler - mockClient.On("Connect").Return(tc.connectError) - - // QueueEmpty and Close are only called if Connect succeeds and parameters are valid - if tc.connectError == nil && tc.circleID != "" && tc.topic != "" { + // Create a new mock client for each test and setup app + _, mockClient, app := setupTest() // Use setupTest to get handler with mock + + // Setup mock expectations + if tc.circleID != "" && tc.topic != "" { // Only expect call if params are valid mockClient.On("QueueEmpty", tc.circleID, tc.topic).Return(tc.emptyError) - mockClient.On("Close").Return(nil) - } else { - // Close is still called via defer even if we return early - mockClient.On("Close").Return(nil).Maybe() } - - // Create a new handler with the mock client - handler := &JobHandler{ - client: mockClient, - } - - // Create a new app for each test - app := fiber.New() - api := app.Group("/api") - jobs := api.Group("/jobs") - jobs.Post("/queue/empty", handler.queueEmpty) - + // Create request body reqBody := map[string]string{ "circleid": tc.circleID, @@ -221,24 +164,24 @@ func TestQueueEmpty(t *testing.T) { } reqBodyBytes, err := json.Marshal(reqBody) assert.NoError(t, err) - + // Create test request req, err := createTestRequest(http.MethodPost, "/api/jobs/queue/empty", bytes.NewReader(reqBodyBytes)) assert.NoError(t, err) req.Header.Set("Content-Type", "application/json") - + // Perform the request resp, err := app.Test(req) assert.NoError(t, err) - + // Check status code assert.Equal(t, tc.expectedStatus, resp.StatusCode) - + // Check response body body, err := io.ReadAll(resp.Body) assert.NoError(t, err) assert.JSONEq(t, tc.expectedBody, string(body)) - + // Verify that all expectations were met mockClient.AssertExpectations(t) }) @@ -248,61 +191,80 @@ func TestQueueEmpty(t *testing.T) { // TestQueueGet tests the queueGet handler func TestQueueGet(t *testing.T) { // Create a test job - testJob := &herojobs.Job{ - JobID: "test-job-id", - CircleID: "test-circle", - Topic: "test-topic", - } - + testJob := herojobs.NewJob() + testJob.JobID = 10 // This will be a number in JSON + testJob.CircleID = "test-circle" + testJob.Topic = "test-topic" + testJob.Params = "some script" + testJob.ParamsType = herojobs.ParamsTypeHeroScript + testJob.Status = herojobs.JobStatusNew + // Test cases tests := []struct { name string circleID string topic string - connectError error - getError error - getResponse *herojobs.Job + listJobsError error + listJobsResp []uint32 + getJobError error + getJobResp *herojobs.Job expectedStatus int - expectedBody string + expectedBody string // This will need to be updated to match the actual job structure }{ { name: "Success", circleID: "test-circle", topic: "test-topic", - connectError: nil, - getError: nil, - getResponse: testJob, + listJobsError: nil, + listJobsResp: []uint32{10}, + getJobError: nil, + getJobResp: testJob, expectedStatus: fiber.StatusOK, - // Include all fields in the response, even empty ones - expectedBody: `{"jobid":"test-job-id","circleid":"test-circle","topic":"test-topic","error":"","heroscript":"","result":"","rhaiscript":"","sessionkey":"","status":"","time_end":0,"time_scheduled":0,"time_start":0,"timeout":0}`, + expectedBody: `{"jobid":10,"circleid":"test-circle","topic":"test-topic","params":"some script","paramstype":"HeroScript","status":"new","sessionkey":"","result":"","error":"","timeout":60,"log":false,"timescheduled":0,"timestart":0,"timeend":0}`, }, + // Removed "Connection Error" { - name: "Connection Error", + name: "ListJobs Error", circleID: "test-circle", topic: "test-topic", - connectError: errors.New("connection error"), - getError: nil, - getResponse: nil, + listJobsError: errors.New("list error"), + listJobsResp: nil, + getJobError: nil, // Not reached + getJobResp: nil, // Not reached expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to connect to HeroJobs server: connection error"}`, + expectedBody: `{"error":"Failed to list jobs in queue: list error"}`, }, { - name: "Get Error", + name: "GetJob Error after ListJobs success", circleID: "test-circle", topic: "test-topic", - connectError: nil, - getError: errors.New("get error"), - getResponse: nil, - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to get job from queue: get error"}`, + listJobsError: nil, + listJobsResp: []uint32{10}, + getJobError: errors.New("get error"), + getJobResp: nil, + expectedStatus: fiber.StatusInternalServerError, // Or based on how GetJob error is handled (e.g. fallback to OurDB) + // The error message might be more complex if OurDB load is also attempted and fails + expectedBody: `{"error":"Failed to get job 10 from queue (Redis err: get error / OurDB err: record not found)"}`, // Adjusted expected error + }, + { + name: "Queue Empty (ListJobs returns empty)", + circleID: "test-circle", + topic: "test-topic", + listJobsError: nil, + listJobsResp: []uint32{}, // Empty list + getJobError: nil, + getJobResp: nil, + expectedStatus: fiber.StatusNotFound, + expectedBody: `{"error":"Queue is empty or no jobs found"}`, }, { name: "Empty Circle ID", circleID: "", topic: "test-topic", - connectError: nil, - getError: nil, - getResponse: nil, + listJobsError: nil, + listJobsResp: nil, + getJobError: nil, + getJobResp: nil, expectedStatus: fiber.StatusBadRequest, expectedBody: `{"error":"Circle ID is required"}`, }, @@ -310,59 +272,50 @@ func TestQueueGet(t *testing.T) { name: "Empty Topic", circleID: "test-circle", topic: "", - connectError: nil, - getError: nil, - getResponse: nil, + listJobsError: nil, + listJobsResp: nil, + getJobError: nil, + getJobResp: nil, expectedStatus: fiber.StatusBadRequest, expectedBody: `{"error":"Topic is required"}`, }, } - + for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - // Create a new mock client for each test - mockClient := new(MockHeroJobsClient) - - // Setup mock expectations - Connect is always called in the handler - mockClient.On("Connect").Return(tc.connectError) - - // QueueGet and Close are only called if Connect succeeds and parameters are valid - if tc.connectError == nil && tc.circleID != "" && tc.topic != "" { - mockClient.On("QueueGet", tc.circleID, tc.topic).Return(tc.getResponse, tc.getError) - mockClient.On("Close").Return(nil) - } else { - // Close is still called via defer even if we return early - mockClient.On("Close").Return(nil).Maybe() + // Create a new mock client for each test and setup app + _, mockClient, app := setupTest() + + // Setup mock expectations + if tc.circleID != "" && tc.topic != "" { + mockClient.On("ListJobs", tc.circleID, tc.topic).Return(tc.listJobsResp, tc.listJobsError) + if tc.listJobsError == nil && len(tc.listJobsResp) > 0 { + // Expect GetJob to be called with the first ID from listJobsResp + // The handler passes uint32 to client.GetJob, which matches interface{} + mockClient.On("GetJob", tc.listJobsResp[0]).Return(tc.getJobResp, tc.getJobError).Maybe() + // If GetJob from Redis fails, a Load from OurDB is attempted. + // We are not mocking job.Load() here as it's on the job object. + // The error message in the test case reflects this potential dual failure. + } } - - // Create a new handler with the mock client - handler := &JobHandler{ - client: mockClient, - } - - // Create a new app for each test - app := fiber.New() - api := app.Group("/api") - jobs := api.Group("/jobs") - jobs.Get("/queue/get", handler.queueGet) - + // Create test request path := fmt.Sprintf("/api/jobs/queue/get?circleid=%s&topic=%s", tc.circleID, tc.topic) req, err := createTestRequest(http.MethodGet, path, nil) assert.NoError(t, err) - + // Perform the request resp, err := app.Test(req) assert.NoError(t, err) - + // Check status code assert.Equal(t, tc.expectedStatus, resp.StatusCode) - + // Check response body body, err := io.ReadAll(resp.Body) assert.NoError(t, err) assert.JSONEq(t, tc.expectedBody, string(body)) - + // Verify that all expectations were met mockClient.AssertExpectations(t) }) @@ -371,150 +324,149 @@ func TestQueueGet(t *testing.T) { // TestCreateJob tests the createJob handler func TestCreateJob(t *testing.T) { - // Create a test job - testJob := &herojobs.Job{ - JobID: "test-job-id", - CircleID: "test-circle", - Topic: "test-topic", - } - // Test cases + createdJob := herojobs.NewJob() + createdJob.JobID = 10 // Assuming Save will populate this; for mock, we set it + createdJob.CircleID = "test-circle" + createdJob.Topic = "test-topic" + createdJob.SessionKey = "test-key" + createdJob.Params = "test-params" + createdJob.ParamsType = herojobs.ParamsTypeHeroScript // Match "HeroScript" string + createdJob.Status = herojobs.JobStatusNew // Default status after NewJob and Save + tests := []struct { name string - circleID string - topic string - sessionKey string - heroScript string - rhaiScript string - connectError error - createError error - createResponse *herojobs.Job + reqBody map[string]interface{} // Use map for flexibility + storeError error + enqueueError error expectedStatus int - expectedBody string + expectedBody string // Will be the createdJob marshaled }{ { - name: "Success", - circleID: "test-circle", - topic: "test-topic", - sessionKey: "test-key", - heroScript: "test-hero-script", - rhaiScript: "test-rhai-script", - connectError: nil, - createError: nil, - createResponse: testJob, + name: "Success", + reqBody: map[string]interface{}{ + "circleid": "test-circle", + "topic": "test-topic", + "sessionkey": "test-key", + "params": "test-params", + "paramstype": "HeroScript", + "timeout": 30, + "log": true, + }, + storeError: nil, + enqueueError: nil, expectedStatus: fiber.StatusOK, - expectedBody: `{"jobid":"test-job-id","circleid":"test-circle","topic":"test-topic","error":"","heroscript":"","result":"","rhaiscript":"","sessionkey":"","status":"","time_end":0,"time_scheduled":0,"time_start":0,"timeout":0}`, + // Expected body should match the 'createdJob' structure after Save, Store, Enqueue + // JobID is assigned by Save(), which we are not mocking here. + // The handler returns the job object. + // For the test, we assume Save() works and populates JobID if it were a real DB. + // The mock will return the job passed to it. + expectedBody: `{"jobid":0,"circleid":"test-circle","topic":"test-topic","params":"test-params","paramstype":"HeroScript","status":"new","sessionkey":"test-key","result":"","error":"","timeout":30,"log":true,"timescheduled":0,"timestart":0,"timeend":0}`, }, + // Removed "Connection Error" { - name: "Connection Error", - circleID: "test-circle", - topic: "test-topic", - sessionKey: "test-key", - heroScript: "test-hero-script", - rhaiScript: "test-rhai-script", - connectError: errors.New("connection error"), - createError: nil, - createResponse: nil, + name: "StoreJob Error", + reqBody: map[string]interface{}{ + "circleid": "test-circle", "topic": "test-topic", "params": "p", "paramstype": "HeroScript", + }, + storeError: errors.New("store error"), + enqueueError: nil, expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to connect to HeroJobs server: connection error"}`, + expectedBody: `{"error":"Failed to store new job in Redis: store error"}`, }, { - name: "Create Error", - circleID: "test-circle", - topic: "test-topic", - sessionKey: "test-key", - heroScript: "test-hero-script", - rhaiScript: "test-rhai-script", - connectError: nil, - createError: errors.New("create error"), - createResponse: nil, + name: "EnqueueJob Error", + reqBody: map[string]interface{}{ + "circleid": "test-circle", "topic": "test-topic", "params": "p", "paramstype": "HeroScript", + }, + storeError: nil, + enqueueError: errors.New("enqueue error"), expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to create job: create error"}`, + expectedBody: `{"error":"Failed to enqueue new job in Redis: enqueue error"}`, }, { - name: "Empty Circle ID", - circleID: "", - topic: "test-topic", - sessionKey: "test-key", - heroScript: "test-hero-script", - rhaiScript: "test-rhai-script", - connectError: nil, - createError: nil, - createResponse: nil, + name: "Empty Circle ID", + reqBody: map[string]interface{}{ + "circleid": "", "topic": "test-topic", "params": "p", "paramstype": "HeroScript", + }, expectedStatus: fiber.StatusBadRequest, expectedBody: `{"error":"Circle ID is required"}`, }, { - name: "Empty Topic", - circleID: "test-circle", - topic: "", - sessionKey: "test-key", - heroScript: "test-hero-script", - rhaiScript: "test-rhai-script", - connectError: nil, - createError: nil, - createResponse: nil, + name: "Empty Topic", + reqBody: map[string]interface{}{ + "circleid": "c", "topic": "", "params": "p", "paramstype": "HeroScript", + }, expectedStatus: fiber.StatusBadRequest, expectedBody: `{"error":"Topic is required"}`, }, + { + name: "Empty Params", + reqBody: map[string]interface{}{ + "circleid": "c", "topic": "t", "params": "", "paramstype": "HeroScript", + }, + expectedStatus: fiber.StatusBadRequest, + expectedBody: `{"error":"Params are required"}`, + }, + { + name: "Empty ParamsType", + reqBody: map[string]interface{}{ + "circleid": "c", "topic": "t", "params": "p", "paramstype": "", + }, + expectedStatus: fiber.StatusBadRequest, + expectedBody: `{"error":"ParamsType is required"}`, + }, + { + name: "Invalid ParamsType", + reqBody: map[string]interface{}{ + "circleid": "c", "topic": "t", "params": "p", "paramstype": "InvalidType", + }, + expectedStatus: fiber.StatusBadRequest, + expectedBody: `{"error":"Invalid ParamsType: InvalidType"}`, + }, } - + for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - // Create a new mock client for each test - mockClient := new(MockHeroJobsClient) - - // Setup mock expectations - Connect is always called in the handler - mockClient.On("Connect").Return(tc.connectError) - - // CreateJob and Close are only called if Connect succeeds and parameters are valid - if tc.connectError == nil && tc.circleID != "" && tc.topic != "" { - mockClient.On("CreateJob", tc.circleID, tc.topic, tc.sessionKey, tc.heroScript, tc.rhaiScript).Return(tc.createResponse, tc.createError) - mockClient.On("Close").Return(nil) - } else { - // Close is still called via defer even if we return early - mockClient.On("Close").Return(nil).Maybe() + _, mockClient, app := setupTest() + + // Setup mock expectations + // job.Save() is called before client interactions. We assume it succeeds for these tests. + // The mock will be called with a job object. We use mock.AnythingOfType for the job + // because the JobID might be populated by Save() in a real scenario, making exact match hard. + if tc.reqBody["circleid"] != "" && tc.reqBody["topic"] != "" && + tc.reqBody["params"] != "" && tc.reqBody["paramstype"] != "" && + herojobs.ParamsType(tc.reqBody["paramstype"].(string)) != "" { // Basic validation check + + // We expect StoreJob to be called with a *herojobs.Job. + // The actual JobID is set by job.Save() which is not mocked here. + // So we use mock.AnythingOfType to match the argument. + mockClient.On("StoreJob", mock.AnythingOfType("*herojobs.Job")).Return(tc.storeError).Once().Maybe() + + if tc.storeError == nil { + mockClient.On("EnqueueJob", mock.AnythingOfType("*herojobs.Job")).Return(tc.enqueueError).Once().Maybe() + } } - - // Create a new handler with the mock client - handler := &JobHandler{ - client: mockClient, - } - - // Create a new app for each test - app := fiber.New() - api := app.Group("/api") - jobs := api.Group("/jobs") - jobs.Post("/create", handler.createJob) - - // Create request body - reqBody := map[string]string{ - "circleid": tc.circleID, - "topic": tc.topic, - "sessionkey": tc.sessionKey, - "heroscript": tc.heroScript, - "rhaiscript": tc.rhaiScript, - } - reqBodyBytes, err := json.Marshal(reqBody) + + reqBodyBytes, err := json.Marshal(tc.reqBody) assert.NoError(t, err) - - // Create test request - req, err := createTestRequest(http.MethodPost, "/api/jobs/create", bytes.NewReader(reqBodyBytes)) + + req, err := createTestRequest(http.MethodPost, "/api/jobs/create", bytes.NewReader(reqBodyBytes)) // Use /api/jobs/create assert.NoError(t, err) - req.Header.Set("Content-Type", "application/json") - + // Content-Type is set by createTestRequest + // Perform the request resp, err := app.Test(req) assert.NoError(t, err) - + // Check status code assert.Equal(t, tc.expectedStatus, resp.StatusCode) - + // Check response body body, err := io.ReadAll(resp.Body) assert.NoError(t, err) assert.JSONEq(t, tc.expectedBody, string(body)) - + // Verify that all expectations were met mockClient.AssertExpectations(t) }) @@ -523,114 +475,96 @@ func TestCreateJob(t *testing.T) { // TestSubmitJob tests the submitJob handler func TestSubmitJob(t *testing.T) { - // Create a test job - testJob := &herojobs.Job{ - JobID: "test-job-id", - CircleID: "test-circle", - Topic: "test-topic", - } - // Test cases + submittedJob := herojobs.NewJob() + submittedJob.JobID = 10 // Assume Save populates this + submittedJob.CircleID = "test-circle" + submittedJob.Topic = "test-topic" + submittedJob.Params = "submitted params" + submittedJob.ParamsType = herojobs.ParamsTypeHeroScript + submittedJob.Status = herojobs.JobStatusNew + tests := []struct { name string - job *herojobs.Job - connectError error - submitError error - submitResponse *herojobs.Job + jobToSubmit *herojobs.Job // This is the job in the request body + storeError error + enqueueError error expectedStatus int - expectedBody string + expectedBody string // Will be the jobToSubmit marshaled (after potential Save) }{ { name: "Success", - job: testJob, - connectError: nil, - submitError: nil, - submitResponse: testJob, + jobToSubmit: submittedJob, + storeError: nil, + enqueueError: nil, expectedStatus: fiber.StatusOK, - expectedBody: `{"jobid":"test-job-id","circleid":"test-circle","topic":"test-topic","error":"","heroscript":"","result":"","rhaiscript":"","sessionkey":"","status":"","time_end":0,"time_scheduled":0,"time_start":0,"timeout":0}`, + // The handler returns the job object from the request after Save(), Store(), Enqueue() + // For the mock, the JobID from jobToSubmit will be used. + expectedBody: `{"jobid":10,"circleid":"test-circle","topic":"test-topic","params":"submitted params","paramstype":"HeroScript","status":"new","sessionkey":"","result":"","error":"","timeout":60,"log":false,"timescheduled":0,"timestart":0,"timeend":0}`, }, + // Removed "Connection Error" { - name: "Connection Error", - job: testJob, - connectError: errors.New("connection error"), - submitError: nil, - submitResponse: nil, + name: "StoreJob Error", + jobToSubmit: submittedJob, + storeError: errors.New("store error"), + enqueueError: nil, expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to connect to HeroJobs server: connection error"}`, + expectedBody: `{"error":"Failed to store job in Redis: store error"}`, }, { - name: "Submit Error", - job: testJob, - connectError: nil, - submitError: errors.New("submit error"), - submitResponse: nil, + name: "EnqueueJob Error", + jobToSubmit: submittedJob, + storeError: nil, + enqueueError: errors.New("enqueue error"), expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to submit job: submit error"}`, + expectedBody: `{"error":"Failed to enqueue job: enqueue error"}`, }, { - name: "Empty Job", - job: nil, - connectError: nil, - submitError: nil, - submitResponse: nil, + name: "Empty Job in request (parsing error)", + jobToSubmit: nil, // Simulates empty or malformed request body expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Failed to parse job data: unexpected end of JSON input"}`, + expectedBody: `{"error":"Failed to parse job data: unexpected end of JSON input"}`, // Or similar based on actual parsing }, } - + for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - // Create a new mock client for each test - mockClient := new(MockHeroJobsClient) - - // Setup mock expectations - Connect is always called in the handler - mockClient.On("Connect").Return(tc.connectError) - - // SubmitJob and Close are only called if Connect succeeds and job is not nil - if tc.connectError == nil && tc.job != nil { - mockClient.On("SubmitJob", tc.job).Return(tc.submitResponse, tc.submitError) - mockClient.On("Close").Return(nil) - } else { - // Close is still called via defer even if we return early - mockClient.On("Close").Return(nil).Maybe() + _, mockClient, app := setupTest() + + // Setup mock expectations + // job.Save() is called before client interactions. + if tc.jobToSubmit != nil { // If job is parsable from request + // We expect StoreJob to be called with the job from the request. + // The JobID might be modified by Save() in a real scenario. + mockClient.On("StoreJob", tc.jobToSubmit).Return(tc.storeError).Once().Maybe() + if tc.storeError == nil { + mockClient.On("EnqueueJob", tc.jobToSubmit).Return(tc.enqueueError).Once().Maybe() + } } - - // Create a new handler with the mock client - handler := &JobHandler{ - client: mockClient, - } - - // Create a new app for each test - app := fiber.New() - api := app.Group("/api") - jobs := api.Group("/jobs") - jobs.Post("/submit", handler.submitJob) - - // Create request body + var reqBodyBytes []byte var err error - if tc.job != nil { - reqBodyBytes, err = json.Marshal(tc.job) + if tc.jobToSubmit != nil { + reqBodyBytes, err = json.Marshal(tc.jobToSubmit) assert.NoError(t, err) } - - // Create test request - req, err := createTestRequest(http.MethodPost, "/api/jobs/submit", bytes.NewReader(reqBodyBytes)) + + req, err := createTestRequest(http.MethodPost, "/api/jobs/submit", bytes.NewReader(reqBodyBytes)) // Use /api/jobs/submit assert.NoError(t, err) - req.Header.Set("Content-Type", "application/json") - + // Content-Type is set by createTestRequest + // Perform the request resp, err := app.Test(req) assert.NoError(t, err) - + // Check status code assert.Equal(t, tc.expectedStatus, resp.StatusCode) - + // Check response body body, err := io.ReadAll(resp.Body) assert.NoError(t, err) assert.JSONEq(t, tc.expectedBody, string(body)) - + // Verify that all expectations were met mockClient.AssertExpectations(t) }) diff --git a/pkg/heroagent/handlers/job_handlers_test.go.bak b/pkg/heroagent/handlers/job_handlers_test.go.bak deleted file mode 100644 index 90b1866..0000000 --- a/pkg/heroagent/handlers/job_handlers_test.go.bak +++ /dev/null @@ -1,4975 +0,0 @@ -package handlers - -import ( - "bytes" - "encoding/json" - "errors" - "fmt" - "io" - "log" - "net/http" - "net/http/httptest" - "testing" - - "github.com/freeflowuniverse/heroagent/pkg/herojobs" - "github.com/gofiber/fiber/v2" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" -) - - - - - -// MockHeroJobsClient is a mock implementation of the HeroJobs client -type MockHeroJobsClient struct { - mock.Mock -} - -// TestQueueEmpty tests the queueEmpty handler -func TestQueueEmpty(t *testing.T) { - // Setup test environment - _, mockClient, app := setupTest() - - // Test cases - tests := []struct { - name string - circleID string - topic string - connectError error - emptyError error - expectedStatus int - expectedBody string - }{ - { - name: "Success", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusOK, - expectedBody: `{"status":"success","message":"Queue for circle test-circle and topic test-topic emptied successfully"}`, - }, - { - name: "Connection Error", - circleID: "test-circle", - topic: "test-topic", - connectError: errors.New("connection error"), - emptyError: nil, - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to connect to HeroJobs server: connection error"}`, - }, - { - name: "Empty Error", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: errors.New("empty error"), - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to empty queue: empty error"}`, - }, - { - name: "Empty Circle ID", - circleID: "", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Circle ID is required"}`, - }, - { - name: "Empty Topic", - circleID: "test-circle", - topic: "", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Topic is required"}`, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - // Setup mock expectations - mockClient.On("Connect").Return(tc.connectError).Maybe() - if tc.connectError == nil && tc.circleID != "" && tc.topic != "" { - mockClient.On("QueueEmpty", tc.circleID, tc.topic).Return(tc.emptyError).Maybe() - mockClient.On("Close").Return(nil).Maybe() - } - - // Create request body - reqBody := map[string]string{ - "circleid": tc.circleID, - "topic": tc.topic, - } - reqBodyBytes, err := json.Marshal(reqBody) - assert.NoError(t, err) - - // Create test request - req, err := createTestRequest(http.MethodPost, "/api/jobs/queue/empty", bytes.NewReader(reqBodyBytes)) - assert.NoError(t, err) - req.Header.Set("Content-Type", "application/json") - - // Perform the request - resp, err := app.Test(req) - assert.NoError(t, err) - - // Check status code - assert.Equal(t, tc.expectedStatus, resp.StatusCode) - - // Check response body - body, err := io.ReadAll(resp.Body) - assert.NoError(t, err) - assert.JSONEq(t, tc.expectedBody, string(body)) - - // Verify that all expectations were met - mockClient.AssertExpectations(t) - }) - } -} - -// Connect mocks the Connect method -func (m *MockHeroJobsClient) Connect() error { - args := m.Called() - return args.Error(0) -} - -// TestQueueEmpty tests the queueEmpty handler -func TestQueueEmpty(t *testing.T) { - // Setup test environment - _, mockClient, app := setupTest() - - // Test cases - tests := []struct { - name string - circleID string - topic string - connectError error - emptyError error - expectedStatus int - expectedBody string - }{ - { - name: "Success", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusOK, - expectedBody: `{"status":"success","message":"Queue for circle test-circle and topic test-topic emptied successfully"}`, - }, - { - name: "Connection Error", - circleID: "test-circle", - topic: "test-topic", - connectError: errors.New("connection error"), - emptyError: nil, - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to connect to HeroJobs server: connection error"}`, - }, - { - name: "Empty Error", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: errors.New("empty error"), - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to empty queue: empty error"}`, - }, - { - name: "Empty Circle ID", - circleID: "", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Circle ID is required"}`, - }, - { - name: "Empty Topic", - circleID: "test-circle", - topic: "", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Topic is required"}`, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - // Setup mock expectations - mockClient.On("Connect").Return(tc.connectError).Maybe() - if tc.connectError == nil && tc.circleID != "" && tc.topic != "" { - mockClient.On("QueueEmpty", tc.circleID, tc.topic).Return(tc.emptyError).Maybe() - mockClient.On("Close").Return(nil).Maybe() - } - - // Create request body - reqBody := map[string]string{ - "circleid": tc.circleID, - "topic": tc.topic, - } - reqBodyBytes, err := json.Marshal(reqBody) - assert.NoError(t, err) - - // Create test request - req, err := createTestRequest(http.MethodPost, "/api/jobs/queue/empty", bytes.NewReader(reqBodyBytes)) - assert.NoError(t, err) - req.Header.Set("Content-Type", "application/json") - - // Perform the request - resp, err := app.Test(req) - assert.NoError(t, err) - - // Check status code - assert.Equal(t, tc.expectedStatus, resp.StatusCode) - - // Check response body - body, err := io.ReadAll(resp.Body) - assert.NoError(t, err) - assert.JSONEq(t, tc.expectedBody, string(body)) - - // Verify that all expectations were met - mockClient.AssertExpectations(t) - }) - } -} - -// Close mocks the Close method -func (m *MockHeroJobsClient) Close() error { - args := m.Called() - return args.Error(0) -} - -// TestQueueEmpty tests the queueEmpty handler -func TestQueueEmpty(t *testing.T) { - // Setup test environment - _, mockClient, app := setupTest() - - // Test cases - tests := []struct { - name string - circleID string - topic string - connectError error - emptyError error - expectedStatus int - expectedBody string - }{ - { - name: "Success", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusOK, - expectedBody: `{"status":"success","message":"Queue for circle test-circle and topic test-topic emptied successfully"}`, - }, - { - name: "Connection Error", - circleID: "test-circle", - topic: "test-topic", - connectError: errors.New("connection error"), - emptyError: nil, - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to connect to HeroJobs server: connection error"}`, - }, - { - name: "Empty Error", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: errors.New("empty error"), - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to empty queue: empty error"}`, - }, - { - name: "Empty Circle ID", - circleID: "", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Circle ID is required"}`, - }, - { - name: "Empty Topic", - circleID: "test-circle", - topic: "", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Topic is required"}`, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - // Setup mock expectations - mockClient.On("Connect").Return(tc.connectError).Maybe() - if tc.connectError == nil && tc.circleID != "" && tc.topic != "" { - mockClient.On("QueueEmpty", tc.circleID, tc.topic).Return(tc.emptyError).Maybe() - mockClient.On("Close").Return(nil).Maybe() - } - - // Create request body - reqBody := map[string]string{ - "circleid": tc.circleID, - "topic": tc.topic, - } - reqBodyBytes, err := json.Marshal(reqBody) - assert.NoError(t, err) - - // Create test request - req, err := createTestRequest(http.MethodPost, "/api/jobs/queue/empty", bytes.NewReader(reqBodyBytes)) - assert.NoError(t, err) - req.Header.Set("Content-Type", "application/json") - - // Perform the request - resp, err := app.Test(req) - assert.NoError(t, err) - - // Check status code - assert.Equal(t, tc.expectedStatus, resp.StatusCode) - - // Check response body - body, err := io.ReadAll(resp.Body) - assert.NoError(t, err) - assert.JSONEq(t, tc.expectedBody, string(body)) - - // Verify that all expectations were met - mockClient.AssertExpectations(t) - }) - } -} - -// SubmitJob mocks the SubmitJob method -func (m *MockHeroJobsClient) SubmitJob(job *herojobs.Job) (*herojobs.Job, error) { - args := m.Called(job) - if args.Get(0) == nil { - return nil, args.Error(1) - } - -// TestQueueEmpty tests the queueEmpty handler -func TestQueueEmpty(t *testing.T) { - // Setup test environment - _, mockClient, app := setupTest() - - // Test cases - tests := []struct { - name string - circleID string - topic string - connectError error - emptyError error - expectedStatus int - expectedBody string - }{ - { - name: "Success", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusOK, - expectedBody: `{"status":"success","message":"Queue for circle test-circle and topic test-topic emptied successfully"}`, - }, - { - name: "Connection Error", - circleID: "test-circle", - topic: "test-topic", - connectError: errors.New("connection error"), - emptyError: nil, - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to connect to HeroJobs server: connection error"}`, - }, - { - name: "Empty Error", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: errors.New("empty error"), - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to empty queue: empty error"}`, - }, - { - name: "Empty Circle ID", - circleID: "", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Circle ID is required"}`, - }, - { - name: "Empty Topic", - circleID: "test-circle", - topic: "", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Topic is required"}`, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - // Setup mock expectations - mockClient.On("Connect").Return(tc.connectError).Maybe() - if tc.connectError == nil && tc.circleID != "" && tc.topic != "" { - mockClient.On("QueueEmpty", tc.circleID, tc.topic).Return(tc.emptyError).Maybe() - mockClient.On("Close").Return(nil).Maybe() - } - - // Create request body - reqBody := map[string]string{ - "circleid": tc.circleID, - "topic": tc.topic, - } - reqBodyBytes, err := json.Marshal(reqBody) - assert.NoError(t, err) - - // Create test request - req, err := createTestRequest(http.MethodPost, "/api/jobs/queue/empty", bytes.NewReader(reqBodyBytes)) - assert.NoError(t, err) - req.Header.Set("Content-Type", "application/json") - - // Perform the request - resp, err := app.Test(req) - assert.NoError(t, err) - - // Check status code - assert.Equal(t, tc.expectedStatus, resp.StatusCode) - - // Check response body - body, err := io.ReadAll(resp.Body) - assert.NoError(t, err) - assert.JSONEq(t, tc.expectedBody, string(body)) - - // Verify that all expectations were met - mockClient.AssertExpectations(t) - }) - } -} - return args.Get(0).(*herojobs.Job), args.Error(1) -} - -// TestQueueEmpty tests the queueEmpty handler -func TestQueueEmpty(t *testing.T) { - // Setup test environment - _, mockClient, app := setupTest() - - // Test cases - tests := []struct { - name string - circleID string - topic string - connectError error - emptyError error - expectedStatus int - expectedBody string - }{ - { - name: "Success", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusOK, - expectedBody: `{"status":"success","message":"Queue for circle test-circle and topic test-topic emptied successfully"}`, - }, - { - name: "Connection Error", - circleID: "test-circle", - topic: "test-topic", - connectError: errors.New("connection error"), - emptyError: nil, - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to connect to HeroJobs server: connection error"}`, - }, - { - name: "Empty Error", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: errors.New("empty error"), - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to empty queue: empty error"}`, - }, - { - name: "Empty Circle ID", - circleID: "", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Circle ID is required"}`, - }, - { - name: "Empty Topic", - circleID: "test-circle", - topic: "", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Topic is required"}`, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - // Setup mock expectations - mockClient.On("Connect").Return(tc.connectError).Maybe() - if tc.connectError == nil && tc.circleID != "" && tc.topic != "" { - mockClient.On("QueueEmpty", tc.circleID, tc.topic).Return(tc.emptyError).Maybe() - mockClient.On("Close").Return(nil).Maybe() - } - - // Create request body - reqBody := map[string]string{ - "circleid": tc.circleID, - "topic": tc.topic, - } - reqBodyBytes, err := json.Marshal(reqBody) - assert.NoError(t, err) - - // Create test request - req, err := createTestRequest(http.MethodPost, "/api/jobs/queue/empty", bytes.NewReader(reqBodyBytes)) - assert.NoError(t, err) - req.Header.Set("Content-Type", "application/json") - - // Perform the request - resp, err := app.Test(req) - assert.NoError(t, err) - - // Check status code - assert.Equal(t, tc.expectedStatus, resp.StatusCode) - - // Check response body - body, err := io.ReadAll(resp.Body) - assert.NoError(t, err) - assert.JSONEq(t, tc.expectedBody, string(body)) - - // Verify that all expectations were met - mockClient.AssertExpectations(t) - }) - } -} - -// GetJob mocks the GetJob method -func (m *MockHeroJobsClient) GetJob(jobID string) (*herojobs.Job, error) { - args := m.Called(jobID) - if args.Get(0) == nil { - return nil, args.Error(1) - } - -// TestQueueEmpty tests the queueEmpty handler -func TestQueueEmpty(t *testing.T) { - // Setup test environment - _, mockClient, app := setupTest() - - // Test cases - tests := []struct { - name string - circleID string - topic string - connectError error - emptyError error - expectedStatus int - expectedBody string - }{ - { - name: "Success", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusOK, - expectedBody: `{"status":"success","message":"Queue for circle test-circle and topic test-topic emptied successfully"}`, - }, - { - name: "Connection Error", - circleID: "test-circle", - topic: "test-topic", - connectError: errors.New("connection error"), - emptyError: nil, - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to connect to HeroJobs server: connection error"}`, - }, - { - name: "Empty Error", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: errors.New("empty error"), - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to empty queue: empty error"}`, - }, - { - name: "Empty Circle ID", - circleID: "", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Circle ID is required"}`, - }, - { - name: "Empty Topic", - circleID: "test-circle", - topic: "", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Topic is required"}`, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - // Setup mock expectations - mockClient.On("Connect").Return(tc.connectError).Maybe() - if tc.connectError == nil && tc.circleID != "" && tc.topic != "" { - mockClient.On("QueueEmpty", tc.circleID, tc.topic).Return(tc.emptyError).Maybe() - mockClient.On("Close").Return(nil).Maybe() - } - - // Create request body - reqBody := map[string]string{ - "circleid": tc.circleID, - "topic": tc.topic, - } - reqBodyBytes, err := json.Marshal(reqBody) - assert.NoError(t, err) - - // Create test request - req, err := createTestRequest(http.MethodPost, "/api/jobs/queue/empty", bytes.NewReader(reqBodyBytes)) - assert.NoError(t, err) - req.Header.Set("Content-Type", "application/json") - - // Perform the request - resp, err := app.Test(req) - assert.NoError(t, err) - - // Check status code - assert.Equal(t, tc.expectedStatus, resp.StatusCode) - - // Check response body - body, err := io.ReadAll(resp.Body) - assert.NoError(t, err) - assert.JSONEq(t, tc.expectedBody, string(body)) - - // Verify that all expectations were met - mockClient.AssertExpectations(t) - }) - } -} - return args.Get(0).(*herojobs.Job), args.Error(1) -} - -// TestQueueEmpty tests the queueEmpty handler -func TestQueueEmpty(t *testing.T) { - // Setup test environment - _, mockClient, app := setupTest() - - // Test cases - tests := []struct { - name string - circleID string - topic string - connectError error - emptyError error - expectedStatus int - expectedBody string - }{ - { - name: "Success", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusOK, - expectedBody: `{"status":"success","message":"Queue for circle test-circle and topic test-topic emptied successfully"}`, - }, - { - name: "Connection Error", - circleID: "test-circle", - topic: "test-topic", - connectError: errors.New("connection error"), - emptyError: nil, - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to connect to HeroJobs server: connection error"}`, - }, - { - name: "Empty Error", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: errors.New("empty error"), - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to empty queue: empty error"}`, - }, - { - name: "Empty Circle ID", - circleID: "", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Circle ID is required"}`, - }, - { - name: "Empty Topic", - circleID: "test-circle", - topic: "", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Topic is required"}`, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - // Setup mock expectations - mockClient.On("Connect").Return(tc.connectError).Maybe() - if tc.connectError == nil && tc.circleID != "" && tc.topic != "" { - mockClient.On("QueueEmpty", tc.circleID, tc.topic).Return(tc.emptyError).Maybe() - mockClient.On("Close").Return(nil).Maybe() - } - - // Create request body - reqBody := map[string]string{ - "circleid": tc.circleID, - "topic": tc.topic, - } - reqBodyBytes, err := json.Marshal(reqBody) - assert.NoError(t, err) - - // Create test request - req, err := createTestRequest(http.MethodPost, "/api/jobs/queue/empty", bytes.NewReader(reqBodyBytes)) - assert.NoError(t, err) - req.Header.Set("Content-Type", "application/json") - - // Perform the request - resp, err := app.Test(req) - assert.NoError(t, err) - - // Check status code - assert.Equal(t, tc.expectedStatus, resp.StatusCode) - - // Check response body - body, err := io.ReadAll(resp.Body) - assert.NoError(t, err) - assert.JSONEq(t, tc.expectedBody, string(body)) - - // Verify that all expectations were met - mockClient.AssertExpectations(t) - }) - } -} - -// DeleteJob mocks the DeleteJob method -func (m *MockHeroJobsClient) DeleteJob(jobID string) error { - args := m.Called(jobID) - return args.Error(0) -} - -// TestQueueEmpty tests the queueEmpty handler -func TestQueueEmpty(t *testing.T) { - // Setup test environment - _, mockClient, app := setupTest() - - // Test cases - tests := []struct { - name string - circleID string - topic string - connectError error - emptyError error - expectedStatus int - expectedBody string - }{ - { - name: "Success", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusOK, - expectedBody: `{"status":"success","message":"Queue for circle test-circle and topic test-topic emptied successfully"}`, - }, - { - name: "Connection Error", - circleID: "test-circle", - topic: "test-topic", - connectError: errors.New("connection error"), - emptyError: nil, - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to connect to HeroJobs server: connection error"}`, - }, - { - name: "Empty Error", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: errors.New("empty error"), - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to empty queue: empty error"}`, - }, - { - name: "Empty Circle ID", - circleID: "", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Circle ID is required"}`, - }, - { - name: "Empty Topic", - circleID: "test-circle", - topic: "", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Topic is required"}`, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - // Setup mock expectations - mockClient.On("Connect").Return(tc.connectError).Maybe() - if tc.connectError == nil && tc.circleID != "" && tc.topic != "" { - mockClient.On("QueueEmpty", tc.circleID, tc.topic).Return(tc.emptyError).Maybe() - mockClient.On("Close").Return(nil).Maybe() - } - - // Create request body - reqBody := map[string]string{ - "circleid": tc.circleID, - "topic": tc.topic, - } - reqBodyBytes, err := json.Marshal(reqBody) - assert.NoError(t, err) - - // Create test request - req, err := createTestRequest(http.MethodPost, "/api/jobs/queue/empty", bytes.NewReader(reqBodyBytes)) - assert.NoError(t, err) - req.Header.Set("Content-Type", "application/json") - - // Perform the request - resp, err := app.Test(req) - assert.NoError(t, err) - - // Check status code - assert.Equal(t, tc.expectedStatus, resp.StatusCode) - - // Check response body - body, err := io.ReadAll(resp.Body) - assert.NoError(t, err) - assert.JSONEq(t, tc.expectedBody, string(body)) - - // Verify that all expectations were met - mockClient.AssertExpectations(t) - }) - } -} - -// ListJobs mocks the ListJobs method -func (m *MockHeroJobsClient) ListJobs(circleID, topic string) ([]string, error) { - args := m.Called(circleID, topic) - if args.Get(0) == nil { - return nil, args.Error(1) - } - -// TestQueueEmpty tests the queueEmpty handler -func TestQueueEmpty(t *testing.T) { - // Setup test environment - _, mockClient, app := setupTest() - - // Test cases - tests := []struct { - name string - circleID string - topic string - connectError error - emptyError error - expectedStatus int - expectedBody string - }{ - { - name: "Success", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusOK, - expectedBody: `{"status":"success","message":"Queue for circle test-circle and topic test-topic emptied successfully"}`, - }, - { - name: "Connection Error", - circleID: "test-circle", - topic: "test-topic", - connectError: errors.New("connection error"), - emptyError: nil, - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to connect to HeroJobs server: connection error"}`, - }, - { - name: "Empty Error", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: errors.New("empty error"), - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to empty queue: empty error"}`, - }, - { - name: "Empty Circle ID", - circleID: "", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Circle ID is required"}`, - }, - { - name: "Empty Topic", - circleID: "test-circle", - topic: "", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Topic is required"}`, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - // Setup mock expectations - mockClient.On("Connect").Return(tc.connectError).Maybe() - if tc.connectError == nil && tc.circleID != "" && tc.topic != "" { - mockClient.On("QueueEmpty", tc.circleID, tc.topic).Return(tc.emptyError).Maybe() - mockClient.On("Close").Return(nil).Maybe() - } - - // Create request body - reqBody := map[string]string{ - "circleid": tc.circleID, - "topic": tc.topic, - } - reqBodyBytes, err := json.Marshal(reqBody) - assert.NoError(t, err) - - // Create test request - req, err := createTestRequest(http.MethodPost, "/api/jobs/queue/empty", bytes.NewReader(reqBodyBytes)) - assert.NoError(t, err) - req.Header.Set("Content-Type", "application/json") - - // Perform the request - resp, err := app.Test(req) - assert.NoError(t, err) - - // Check status code - assert.Equal(t, tc.expectedStatus, resp.StatusCode) - - // Check response body - body, err := io.ReadAll(resp.Body) - assert.NoError(t, err) - assert.JSONEq(t, tc.expectedBody, string(body)) - - // Verify that all expectations were met - mockClient.AssertExpectations(t) - }) - } -} - return args.Get(0).([]string), args.Error(1) -} - -// TestQueueEmpty tests the queueEmpty handler -func TestQueueEmpty(t *testing.T) { - // Setup test environment - _, mockClient, app := setupTest() - - // Test cases - tests := []struct { - name string - circleID string - topic string - connectError error - emptyError error - expectedStatus int - expectedBody string - }{ - { - name: "Success", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusOK, - expectedBody: `{"status":"success","message":"Queue for circle test-circle and topic test-topic emptied successfully"}`, - }, - { - name: "Connection Error", - circleID: "test-circle", - topic: "test-topic", - connectError: errors.New("connection error"), - emptyError: nil, - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to connect to HeroJobs server: connection error"}`, - }, - { - name: "Empty Error", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: errors.New("empty error"), - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to empty queue: empty error"}`, - }, - { - name: "Empty Circle ID", - circleID: "", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Circle ID is required"}`, - }, - { - name: "Empty Topic", - circleID: "test-circle", - topic: "", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Topic is required"}`, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - // Setup mock expectations - mockClient.On("Connect").Return(tc.connectError).Maybe() - if tc.connectError == nil && tc.circleID != "" && tc.topic != "" { - mockClient.On("QueueEmpty", tc.circleID, tc.topic).Return(tc.emptyError).Maybe() - mockClient.On("Close").Return(nil).Maybe() - } - - // Create request body - reqBody := map[string]string{ - "circleid": tc.circleID, - "topic": tc.topic, - } - reqBodyBytes, err := json.Marshal(reqBody) - assert.NoError(t, err) - - // Create test request - req, err := createTestRequest(http.MethodPost, "/api/jobs/queue/empty", bytes.NewReader(reqBodyBytes)) - assert.NoError(t, err) - req.Header.Set("Content-Type", "application/json") - - // Perform the request - resp, err := app.Test(req) - assert.NoError(t, err) - - // Check status code - assert.Equal(t, tc.expectedStatus, resp.StatusCode) - - // Check response body - body, err := io.ReadAll(resp.Body) - assert.NoError(t, err) - assert.JSONEq(t, tc.expectedBody, string(body)) - - // Verify that all expectations were met - mockClient.AssertExpectations(t) - }) - } -} - -// QueueSize mocks the QueueSize method -func (m *MockHeroJobsClient) QueueSize(circleID, topic string) (int64, error) { - args := m.Called(circleID, topic) - return args.Get(0).(int64), args.Error(1) -} - -// TestQueueEmpty tests the queueEmpty handler -func TestQueueEmpty(t *testing.T) { - // Setup test environment - _, mockClient, app := setupTest() - - // Test cases - tests := []struct { - name string - circleID string - topic string - connectError error - emptyError error - expectedStatus int - expectedBody string - }{ - { - name: "Success", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusOK, - expectedBody: `{"status":"success","message":"Queue for circle test-circle and topic test-topic emptied successfully"}`, - }, - { - name: "Connection Error", - circleID: "test-circle", - topic: "test-topic", - connectError: errors.New("connection error"), - emptyError: nil, - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to connect to HeroJobs server: connection error"}`, - }, - { - name: "Empty Error", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: errors.New("empty error"), - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to empty queue: empty error"}`, - }, - { - name: "Empty Circle ID", - circleID: "", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Circle ID is required"}`, - }, - { - name: "Empty Topic", - circleID: "test-circle", - topic: "", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Topic is required"}`, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - // Setup mock expectations - mockClient.On("Connect").Return(tc.connectError).Maybe() - if tc.connectError == nil && tc.circleID != "" && tc.topic != "" { - mockClient.On("QueueEmpty", tc.circleID, tc.topic).Return(tc.emptyError).Maybe() - mockClient.On("Close").Return(nil).Maybe() - } - - // Create request body - reqBody := map[string]string{ - "circleid": tc.circleID, - "topic": tc.topic, - } - reqBodyBytes, err := json.Marshal(reqBody) - assert.NoError(t, err) - - // Create test request - req, err := createTestRequest(http.MethodPost, "/api/jobs/queue/empty", bytes.NewReader(reqBodyBytes)) - assert.NoError(t, err) - req.Header.Set("Content-Type", "application/json") - - // Perform the request - resp, err := app.Test(req) - assert.NoError(t, err) - - // Check status code - assert.Equal(t, tc.expectedStatus, resp.StatusCode) - - // Check response body - body, err := io.ReadAll(resp.Body) - assert.NoError(t, err) - assert.JSONEq(t, tc.expectedBody, string(body)) - - // Verify that all expectations were met - mockClient.AssertExpectations(t) - }) - } -} - -// QueueEmpty mocks the QueueEmpty method -func (m *MockHeroJobsClient) QueueEmpty(circleID, topic string) error { - args := m.Called(circleID, topic) - return args.Error(0) -} - -// TestQueueEmpty tests the queueEmpty handler -func TestQueueEmpty(t *testing.T) { - // Setup test environment - _, mockClient, app := setupTest() - - // Test cases - tests := []struct { - name string - circleID string - topic string - connectError error - emptyError error - expectedStatus int - expectedBody string - }{ - { - name: "Success", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusOK, - expectedBody: `{"status":"success","message":"Queue for circle test-circle and topic test-topic emptied successfully"}`, - }, - { - name: "Connection Error", - circleID: "test-circle", - topic: "test-topic", - connectError: errors.New("connection error"), - emptyError: nil, - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to connect to HeroJobs server: connection error"}`, - }, - { - name: "Empty Error", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: errors.New("empty error"), - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to empty queue: empty error"}`, - }, - { - name: "Empty Circle ID", - circleID: "", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Circle ID is required"}`, - }, - { - name: "Empty Topic", - circleID: "test-circle", - topic: "", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Topic is required"}`, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - // Setup mock expectations - mockClient.On("Connect").Return(tc.connectError).Maybe() - if tc.connectError == nil && tc.circleID != "" && tc.topic != "" { - mockClient.On("QueueEmpty", tc.circleID, tc.topic).Return(tc.emptyError).Maybe() - mockClient.On("Close").Return(nil).Maybe() - } - - // Create request body - reqBody := map[string]string{ - "circleid": tc.circleID, - "topic": tc.topic, - } - reqBodyBytes, err := json.Marshal(reqBody) - assert.NoError(t, err) - - // Create test request - req, err := createTestRequest(http.MethodPost, "/api/jobs/queue/empty", bytes.NewReader(reqBodyBytes)) - assert.NoError(t, err) - req.Header.Set("Content-Type", "application/json") - - // Perform the request - resp, err := app.Test(req) - assert.NoError(t, err) - - // Check status code - assert.Equal(t, tc.expectedStatus, resp.StatusCode) - - // Check response body - body, err := io.ReadAll(resp.Body) - assert.NoError(t, err) - assert.JSONEq(t, tc.expectedBody, string(body)) - - // Verify that all expectations were met - mockClient.AssertExpectations(t) - }) - } -} - -// QueueGet mocks the QueueGet method -func (m *MockHeroJobsClient) QueueGet(circleID, topic string) (*herojobs.Job, error) { - args := m.Called(circleID, topic) - if args.Get(0) == nil { - return nil, args.Error(1) - } - -// TestQueueEmpty tests the queueEmpty handler -func TestQueueEmpty(t *testing.T) { - // Setup test environment - _, mockClient, app := setupTest() - - // Test cases - tests := []struct { - name string - circleID string - topic string - connectError error - emptyError error - expectedStatus int - expectedBody string - }{ - { - name: "Success", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusOK, - expectedBody: `{"status":"success","message":"Queue for circle test-circle and topic test-topic emptied successfully"}`, - }, - { - name: "Connection Error", - circleID: "test-circle", - topic: "test-topic", - connectError: errors.New("connection error"), - emptyError: nil, - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to connect to HeroJobs server: connection error"}`, - }, - { - name: "Empty Error", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: errors.New("empty error"), - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to empty queue: empty error"}`, - }, - { - name: "Empty Circle ID", - circleID: "", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Circle ID is required"}`, - }, - { - name: "Empty Topic", - circleID: "test-circle", - topic: "", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Topic is required"}`, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - // Setup mock expectations - mockClient.On("Connect").Return(tc.connectError).Maybe() - if tc.connectError == nil && tc.circleID != "" && tc.topic != "" { - mockClient.On("QueueEmpty", tc.circleID, tc.topic).Return(tc.emptyError).Maybe() - mockClient.On("Close").Return(nil).Maybe() - } - - // Create request body - reqBody := map[string]string{ - "circleid": tc.circleID, - "topic": tc.topic, - } - reqBodyBytes, err := json.Marshal(reqBody) - assert.NoError(t, err) - - // Create test request - req, err := createTestRequest(http.MethodPost, "/api/jobs/queue/empty", bytes.NewReader(reqBodyBytes)) - assert.NoError(t, err) - req.Header.Set("Content-Type", "application/json") - - // Perform the request - resp, err := app.Test(req) - assert.NoError(t, err) - - // Check status code - assert.Equal(t, tc.expectedStatus, resp.StatusCode) - - // Check response body - body, err := io.ReadAll(resp.Body) - assert.NoError(t, err) - assert.JSONEq(t, tc.expectedBody, string(body)) - - // Verify that all expectations were met - mockClient.AssertExpectations(t) - }) - } -} - return args.Get(0).(*herojobs.Job), args.Error(1) -} - -// TestQueueEmpty tests the queueEmpty handler -func TestQueueEmpty(t *testing.T) { - // Setup test environment - _, mockClient, app := setupTest() - - // Test cases - tests := []struct { - name string - circleID string - topic string - connectError error - emptyError error - expectedStatus int - expectedBody string - }{ - { - name: "Success", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusOK, - expectedBody: `{"status":"success","message":"Queue for circle test-circle and topic test-topic emptied successfully"}`, - }, - { - name: "Connection Error", - circleID: "test-circle", - topic: "test-topic", - connectError: errors.New("connection error"), - emptyError: nil, - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to connect to HeroJobs server: connection error"}`, - }, - { - name: "Empty Error", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: errors.New("empty error"), - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to empty queue: empty error"}`, - }, - { - name: "Empty Circle ID", - circleID: "", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Circle ID is required"}`, - }, - { - name: "Empty Topic", - circleID: "test-circle", - topic: "", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Topic is required"}`, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - // Setup mock expectations - mockClient.On("Connect").Return(tc.connectError).Maybe() - if tc.connectError == nil && tc.circleID != "" && tc.topic != "" { - mockClient.On("QueueEmpty", tc.circleID, tc.topic).Return(tc.emptyError).Maybe() - mockClient.On("Close").Return(nil).Maybe() - } - - // Create request body - reqBody := map[string]string{ - "circleid": tc.circleID, - "topic": tc.topic, - } - reqBodyBytes, err := json.Marshal(reqBody) - assert.NoError(t, err) - - // Create test request - req, err := createTestRequest(http.MethodPost, "/api/jobs/queue/empty", bytes.NewReader(reqBodyBytes)) - assert.NoError(t, err) - req.Header.Set("Content-Type", "application/json") - - // Perform the request - resp, err := app.Test(req) - assert.NoError(t, err) - - // Check status code - assert.Equal(t, tc.expectedStatus, resp.StatusCode) - - // Check response body - body, err := io.ReadAll(resp.Body) - assert.NoError(t, err) - assert.JSONEq(t, tc.expectedBody, string(body)) - - // Verify that all expectations were met - mockClient.AssertExpectations(t) - }) - } -} - -// CreateJob mocks the CreateJob method -func (m *MockHeroJobsClient) CreateJob(circleID, topic, sessionKey, heroScript, rhaiScript string) (*herojobs.Job, error) { - args := m.Called(circleID, topic, sessionKey, heroScript, rhaiScript) - if args.Get(0) == nil { - return nil, args.Error(1) - } - -// TestQueueEmpty tests the queueEmpty handler -func TestQueueEmpty(t *testing.T) { - // Setup test environment - _, mockClient, app := setupTest() - - // Test cases - tests := []struct { - name string - circleID string - topic string - connectError error - emptyError error - expectedStatus int - expectedBody string - }{ - { - name: "Success", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusOK, - expectedBody: `{"status":"success","message":"Queue for circle test-circle and topic test-topic emptied successfully"}`, - }, - { - name: "Connection Error", - circleID: "test-circle", - topic: "test-topic", - connectError: errors.New("connection error"), - emptyError: nil, - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to connect to HeroJobs server: connection error"}`, - }, - { - name: "Empty Error", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: errors.New("empty error"), - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to empty queue: empty error"}`, - }, - { - name: "Empty Circle ID", - circleID: "", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Circle ID is required"}`, - }, - { - name: "Empty Topic", - circleID: "test-circle", - topic: "", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Topic is required"}`, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - // Setup mock expectations - mockClient.On("Connect").Return(tc.connectError).Maybe() - if tc.connectError == nil && tc.circleID != "" && tc.topic != "" { - mockClient.On("QueueEmpty", tc.circleID, tc.topic).Return(tc.emptyError).Maybe() - mockClient.On("Close").Return(nil).Maybe() - } - - // Create request body - reqBody := map[string]string{ - "circleid": tc.circleID, - "topic": tc.topic, - } - reqBodyBytes, err := json.Marshal(reqBody) - assert.NoError(t, err) - - // Create test request - req, err := createTestRequest(http.MethodPost, "/api/jobs/queue/empty", bytes.NewReader(reqBodyBytes)) - assert.NoError(t, err) - req.Header.Set("Content-Type", "application/json") - - // Perform the request - resp, err := app.Test(req) - assert.NoError(t, err) - - // Check status code - assert.Equal(t, tc.expectedStatus, resp.StatusCode) - - // Check response body - body, err := io.ReadAll(resp.Body) - assert.NoError(t, err) - assert.JSONEq(t, tc.expectedBody, string(body)) - - // Verify that all expectations were met - mockClient.AssertExpectations(t) - }) - } -} - return args.Get(0).(*herojobs.Job), args.Error(1) -} - -// TestQueueEmpty tests the queueEmpty handler -func TestQueueEmpty(t *testing.T) { - // Setup test environment - _, mockClient, app := setupTest() - - // Test cases - tests := []struct { - name string - circleID string - topic string - connectError error - emptyError error - expectedStatus int - expectedBody string - }{ - { - name: "Success", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusOK, - expectedBody: `{"status":"success","message":"Queue for circle test-circle and topic test-topic emptied successfully"}`, - }, - { - name: "Connection Error", - circleID: "test-circle", - topic: "test-topic", - connectError: errors.New("connection error"), - emptyError: nil, - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to connect to HeroJobs server: connection error"}`, - }, - { - name: "Empty Error", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: errors.New("empty error"), - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to empty queue: empty error"}`, - }, - { - name: "Empty Circle ID", - circleID: "", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Circle ID is required"}`, - }, - { - name: "Empty Topic", - circleID: "test-circle", - topic: "", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Topic is required"}`, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - // Setup mock expectations - mockClient.On("Connect").Return(tc.connectError).Maybe() - if tc.connectError == nil && tc.circleID != "" && tc.topic != "" { - mockClient.On("QueueEmpty", tc.circleID, tc.topic).Return(tc.emptyError).Maybe() - mockClient.On("Close").Return(nil).Maybe() - } - - // Create request body - reqBody := map[string]string{ - "circleid": tc.circleID, - "topic": tc.topic, - } - reqBodyBytes, err := json.Marshal(reqBody) - assert.NoError(t, err) - - // Create test request - req, err := createTestRequest(http.MethodPost, "/api/jobs/queue/empty", bytes.NewReader(reqBodyBytes)) - assert.NoError(t, err) - req.Header.Set("Content-Type", "application/json") - - // Perform the request - resp, err := app.Test(req) - assert.NoError(t, err) - - // Check status code - assert.Equal(t, tc.expectedStatus, resp.StatusCode) - - // Check response body - body, err := io.ReadAll(resp.Body) - assert.NoError(t, err) - assert.JSONEq(t, tc.expectedBody, string(body)) - - // Verify that all expectations were met - mockClient.AssertExpectations(t) - }) - } -} - -// setupTest initializes a test environment with a mock client -func setupTest() (*JobHandler, *MockHeroJobsClient, *fiber.App) { - mockClient := new(MockHeroJobsClient) - - // Create a JobHandler with the mock client - handler := &JobHandler{ - client: mockClient, - logger: nil, // We don't need a real logger for tests - } - -// TestQueueEmpty tests the queueEmpty handler -func TestQueueEmpty(t *testing.T) { - // Setup test environment - _, mockClient, app := setupTest() - - // Test cases - tests := []struct { - name string - circleID string - topic string - connectError error - emptyError error - expectedStatus int - expectedBody string - }{ - { - name: "Success", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusOK, - expectedBody: `{"status":"success","message":"Queue for circle test-circle and topic test-topic emptied successfully"}`, - }, - { - name: "Connection Error", - circleID: "test-circle", - topic: "test-topic", - connectError: errors.New("connection error"), - emptyError: nil, - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to connect to HeroJobs server: connection error"}`, - }, - { - name: "Empty Error", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: errors.New("empty error"), - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to empty queue: empty error"}`, - }, - { - name: "Empty Circle ID", - circleID: "", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Circle ID is required"}`, - }, - { - name: "Empty Topic", - circleID: "test-circle", - topic: "", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Topic is required"}`, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - // Setup mock expectations - mockClient.On("Connect").Return(tc.connectError).Maybe() - if tc.connectError == nil && tc.circleID != "" && tc.topic != "" { - mockClient.On("QueueEmpty", tc.circleID, tc.topic).Return(tc.emptyError).Maybe() - mockClient.On("Close").Return(nil).Maybe() - } - - // Create request body - reqBody := map[string]string{ - "circleid": tc.circleID, - "topic": tc.topic, - } - reqBodyBytes, err := json.Marshal(reqBody) - assert.NoError(t, err) - - // Create test request - req, err := createTestRequest(http.MethodPost, "/api/jobs/queue/empty", bytes.NewReader(reqBodyBytes)) - assert.NoError(t, err) - req.Header.Set("Content-Type", "application/json") - - // Perform the request - resp, err := app.Test(req) - assert.NoError(t, err) - - // Check status code - assert.Equal(t, tc.expectedStatus, resp.StatusCode) - - // Check response body - body, err := io.ReadAll(resp.Body) - assert.NoError(t, err) - assert.JSONEq(t, tc.expectedBody, string(body)) - - // Verify that all expectations were met - mockClient.AssertExpectations(t) - }) - } -} - - // Create a Fiber app for testing - app := fiber.New() - - // Register the routes - handler.RegisterRoutes(app) - - return handler, mockClient, app -} - -// TestQueueEmpty tests the queueEmpty handler -func TestQueueEmpty(t *testing.T) { - // Setup test environment - _, mockClient, app := setupTest() - - // Test cases - tests := []struct { - name string - circleID string - topic string - connectError error - emptyError error - expectedStatus int - expectedBody string - }{ - { - name: "Success", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusOK, - expectedBody: `{"status":"success","message":"Queue for circle test-circle and topic test-topic emptied successfully"}`, - }, - { - name: "Connection Error", - circleID: "test-circle", - topic: "test-topic", - connectError: errors.New("connection error"), - emptyError: nil, - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to connect to HeroJobs server: connection error"}`, - }, - { - name: "Empty Error", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: errors.New("empty error"), - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to empty queue: empty error"}`, - }, - { - name: "Empty Circle ID", - circleID: "", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Circle ID is required"}`, - }, - { - name: "Empty Topic", - circleID: "test-circle", - topic: "", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Topic is required"}`, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - // Setup mock expectations - mockClient.On("Connect").Return(tc.connectError).Maybe() - if tc.connectError == nil && tc.circleID != "" && tc.topic != "" { - mockClient.On("QueueEmpty", tc.circleID, tc.topic).Return(tc.emptyError).Maybe() - mockClient.On("Close").Return(nil).Maybe() - } - - // Create request body - reqBody := map[string]string{ - "circleid": tc.circleID, - "topic": tc.topic, - } - reqBodyBytes, err := json.Marshal(reqBody) - assert.NoError(t, err) - - // Create test request - req, err := createTestRequest(http.MethodPost, "/api/jobs/queue/empty", bytes.NewReader(reqBodyBytes)) - assert.NoError(t, err) - req.Header.Set("Content-Type", "application/json") - - // Perform the request - resp, err := app.Test(req) - assert.NoError(t, err) - - // Check status code - assert.Equal(t, tc.expectedStatus, resp.StatusCode) - - // Check response body - body, err := io.ReadAll(resp.Body) - assert.NoError(t, err) - assert.JSONEq(t, tc.expectedBody, string(body)) - - // Verify that all expectations were met - mockClient.AssertExpectations(t) - }) - } -} - -// createTestRequest creates a test request with the given method, path, and body -func createTestRequest(method, path string, body interface{}) (*http.Request, error) { - var reqBody io.Reader - - if body != nil { - jsonBody, err := json.Marshal(body) - if err != nil { - return nil, err - } - -// TestQueueEmpty tests the queueEmpty handler -func TestQueueEmpty(t *testing.T) { - // Setup test environment - _, mockClient, app := setupTest() - - // Test cases - tests := []struct { - name string - circleID string - topic string - connectError error - emptyError error - expectedStatus int - expectedBody string - }{ - { - name: "Success", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusOK, - expectedBody: `{"status":"success","message":"Queue for circle test-circle and topic test-topic emptied successfully"}`, - }, - { - name: "Connection Error", - circleID: "test-circle", - topic: "test-topic", - connectError: errors.New("connection error"), - emptyError: nil, - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to connect to HeroJobs server: connection error"}`, - }, - { - name: "Empty Error", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: errors.New("empty error"), - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to empty queue: empty error"}`, - }, - { - name: "Empty Circle ID", - circleID: "", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Circle ID is required"}`, - }, - { - name: "Empty Topic", - circleID: "test-circle", - topic: "", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Topic is required"}`, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - // Setup mock expectations - mockClient.On("Connect").Return(tc.connectError).Maybe() - if tc.connectError == nil && tc.circleID != "" && tc.topic != "" { - mockClient.On("QueueEmpty", tc.circleID, tc.topic).Return(tc.emptyError).Maybe() - mockClient.On("Close").Return(nil).Maybe() - } - - // Create request body - reqBody := map[string]string{ - "circleid": tc.circleID, - "topic": tc.topic, - } - reqBodyBytes, err := json.Marshal(reqBody) - assert.NoError(t, err) - - // Create test request - req, err := createTestRequest(http.MethodPost, "/api/jobs/queue/empty", bytes.NewReader(reqBodyBytes)) - assert.NoError(t, err) - req.Header.Set("Content-Type", "application/json") - - // Perform the request - resp, err := app.Test(req) - assert.NoError(t, err) - - // Check status code - assert.Equal(t, tc.expectedStatus, resp.StatusCode) - - // Check response body - body, err := io.ReadAll(resp.Body) - assert.NoError(t, err) - assert.JSONEq(t, tc.expectedBody, string(body)) - - // Verify that all expectations were met - mockClient.AssertExpectations(t) - }) - } -} - reqBody = bytes.NewBuffer(jsonBody) - } - -// TestQueueEmpty tests the queueEmpty handler -func TestQueueEmpty(t *testing.T) { - // Setup test environment - _, mockClient, app := setupTest() - - // Test cases - tests := []struct { - name string - circleID string - topic string - connectError error - emptyError error - expectedStatus int - expectedBody string - }{ - { - name: "Success", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusOK, - expectedBody: `{"status":"success","message":"Queue for circle test-circle and topic test-topic emptied successfully"}`, - }, - { - name: "Connection Error", - circleID: "test-circle", - topic: "test-topic", - connectError: errors.New("connection error"), - emptyError: nil, - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to connect to HeroJobs server: connection error"}`, - }, - { - name: "Empty Error", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: errors.New("empty error"), - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to empty queue: empty error"}`, - }, - { - name: "Empty Circle ID", - circleID: "", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Circle ID is required"}`, - }, - { - name: "Empty Topic", - circleID: "test-circle", - topic: "", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Topic is required"}`, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - // Setup mock expectations - mockClient.On("Connect").Return(tc.connectError).Maybe() - if tc.connectError == nil && tc.circleID != "" && tc.topic != "" { - mockClient.On("QueueEmpty", tc.circleID, tc.topic).Return(tc.emptyError).Maybe() - mockClient.On("Close").Return(nil).Maybe() - } - - // Create request body - reqBody := map[string]string{ - "circleid": tc.circleID, - "topic": tc.topic, - } - reqBodyBytes, err := json.Marshal(reqBody) - assert.NoError(t, err) - - // Create test request - req, err := createTestRequest(http.MethodPost, "/api/jobs/queue/empty", bytes.NewReader(reqBodyBytes)) - assert.NoError(t, err) - req.Header.Set("Content-Type", "application/json") - - // Perform the request - resp, err := app.Test(req) - assert.NoError(t, err) - - // Check status code - assert.Equal(t, tc.expectedStatus, resp.StatusCode) - - // Check response body - body, err := io.ReadAll(resp.Body) - assert.NoError(t, err) - assert.JSONEq(t, tc.expectedBody, string(body)) - - // Verify that all expectations were met - mockClient.AssertExpectations(t) - }) - } -} - - req := httptest.NewRequest(method, path, reqBody) - req.Header.Set("Content-Type", "application/json") - - return req, nil -} - -// TestQueueEmpty tests the queueEmpty handler -func TestQueueEmpty(t *testing.T) { - // Setup test environment - _, mockClient, app := setupTest() - - // Test cases - tests := []struct { - name string - circleID string - topic string - connectError error - emptyError error - expectedStatus int - expectedBody string - }{ - { - name: "Success", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusOK, - expectedBody: `{"status":"success","message":"Queue for circle test-circle and topic test-topic emptied successfully"}`, - }, - { - name: "Connection Error", - circleID: "test-circle", - topic: "test-topic", - connectError: errors.New("connection error"), - emptyError: nil, - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to connect to HeroJobs server: connection error"}`, - }, - { - name: "Empty Error", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: errors.New("empty error"), - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to empty queue: empty error"}`, - }, - { - name: "Empty Circle ID", - circleID: "", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Circle ID is required"}`, - }, - { - name: "Empty Topic", - circleID: "test-circle", - topic: "", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Topic is required"}`, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - // Setup mock expectations - mockClient.On("Connect").Return(tc.connectError).Maybe() - if tc.connectError == nil && tc.circleID != "" && tc.topic != "" { - mockClient.On("QueueEmpty", tc.circleID, tc.topic).Return(tc.emptyError).Maybe() - mockClient.On("Close").Return(nil).Maybe() - } - - // Create request body - reqBody := map[string]string{ - "circleid": tc.circleID, - "topic": tc.topic, - } - reqBodyBytes, err := json.Marshal(reqBody) - assert.NoError(t, err) - - // Create test request - req, err := createTestRequest(http.MethodPost, "/api/jobs/queue/empty", bytes.NewReader(reqBodyBytes)) - assert.NoError(t, err) - req.Header.Set("Content-Type", "application/json") - - // Perform the request - resp, err := app.Test(req) - assert.NoError(t, err) - - // Check status code - assert.Equal(t, tc.expectedStatus, resp.StatusCode) - - // Check response body - body, err := io.ReadAll(resp.Body) - assert.NoError(t, err) - assert.JSONEq(t, tc.expectedBody, string(body)) - - // Verify that all expectations were met - mockClient.AssertExpectations(t) - }) - } -} - -// TestSubmitJob tests the submitJob handler -func TestSubmitJob(t *testing.T) { - // Setup test environment - _, mockClient, app := setupTest() - - // Create a test job - testJob := &herojobs.Job{ - JobID: "test-job-id", - CircleID: "test-circle", - Topic: "test-topic", - } - -// TestQueueEmpty tests the queueEmpty handler -func TestQueueEmpty(t *testing.T) { - // Setup test environment - _, mockClient, app := setupTest() - - // Test cases - tests := []struct { - name string - circleID string - topic string - connectError error - emptyError error - expectedStatus int - expectedBody string - }{ - { - name: "Success", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusOK, - expectedBody: `{"status":"success","message":"Queue for circle test-circle and topic test-topic emptied successfully"}`, - }, - { - name: "Connection Error", - circleID: "test-circle", - topic: "test-topic", - connectError: errors.New("connection error"), - emptyError: nil, - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to connect to HeroJobs server: connection error"}`, - }, - { - name: "Empty Error", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: errors.New("empty error"), - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to empty queue: empty error"}`, - }, - { - name: "Empty Circle ID", - circleID: "", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Circle ID is required"}`, - }, - { - name: "Empty Topic", - circleID: "test-circle", - topic: "", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Topic is required"}`, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - // Setup mock expectations - mockClient.On("Connect").Return(tc.connectError).Maybe() - if tc.connectError == nil && tc.circleID != "" && tc.topic != "" { - mockClient.On("QueueEmpty", tc.circleID, tc.topic).Return(tc.emptyError).Maybe() - mockClient.On("Close").Return(nil).Maybe() - } - - // Create request body - reqBody := map[string]string{ - "circleid": tc.circleID, - "topic": tc.topic, - } - reqBodyBytes, err := json.Marshal(reqBody) - assert.NoError(t, err) - - // Create test request - req, err := createTestRequest(http.MethodPost, "/api/jobs/queue/empty", bytes.NewReader(reqBodyBytes)) - assert.NoError(t, err) - req.Header.Set("Content-Type", "application/json") - - // Perform the request - resp, err := app.Test(req) - assert.NoError(t, err) - - // Check status code - assert.Equal(t, tc.expectedStatus, resp.StatusCode) - - // Check response body - body, err := io.ReadAll(resp.Body) - assert.NoError(t, err) - assert.JSONEq(t, tc.expectedBody, string(body)) - - // Verify that all expectations were met - mockClient.AssertExpectations(t) - }) - } -} - - // Test cases - tests := []struct { - name string - connectError error - submitError error - submitResponse *herojobs.Job - expectedStatus int - expectedBody string - }{ - { - name: "Success", - connectError: nil, - submitError: nil, - submitResponse: testJob, - expectedStatus: fiber.StatusOK, - expectedBody: `{"jobid":"test-job-id","circleid":"test-circle","topic":"test-topic"}`, - }, - { - name: "Connection Error", - connectError: errors.New("connection error"), - submitError: nil, - submitResponse: nil, - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to connect to HeroJobs server: connection error"}`, - }, - { - name: "Submit Error", - connectError: nil, - submitError: errors.New("submit error"), - submitResponse: nil, - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to submit job: submit error"}`, - }, - } - -// TestQueueEmpty tests the queueEmpty handler -func TestQueueEmpty(t *testing.T) { - // Setup test environment - _, mockClient, app := setupTest() - - // Test cases - tests := []struct { - name string - circleID string - topic string - connectError error - emptyError error - expectedStatus int - expectedBody string - }{ - { - name: "Success", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusOK, - expectedBody: `{"status":"success","message":"Queue for circle test-circle and topic test-topic emptied successfully"}`, - }, - { - name: "Connection Error", - circleID: "test-circle", - topic: "test-topic", - connectError: errors.New("connection error"), - emptyError: nil, - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to connect to HeroJobs server: connection error"}`, - }, - { - name: "Empty Error", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: errors.New("empty error"), - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to empty queue: empty error"}`, - }, - { - name: "Empty Circle ID", - circleID: "", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Circle ID is required"}`, - }, - { - name: "Empty Topic", - circleID: "test-circle", - topic: "", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Topic is required"}`, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - // Setup mock expectations - mockClient.On("Connect").Return(tc.connectError).Maybe() - if tc.connectError == nil && tc.circleID != "" && tc.topic != "" { - mockClient.On("QueueEmpty", tc.circleID, tc.topic).Return(tc.emptyError).Maybe() - mockClient.On("Close").Return(nil).Maybe() - } - - // Create request body - reqBody := map[string]string{ - "circleid": tc.circleID, - "topic": tc.topic, - } - reqBodyBytes, err := json.Marshal(reqBody) - assert.NoError(t, err) - - // Create test request - req, err := createTestRequest(http.MethodPost, "/api/jobs/queue/empty", bytes.NewReader(reqBodyBytes)) - assert.NoError(t, err) - req.Header.Set("Content-Type", "application/json") - - // Perform the request - resp, err := app.Test(req) - assert.NoError(t, err) - - // Check status code - assert.Equal(t, tc.expectedStatus, resp.StatusCode) - - // Check response body - body, err := io.ReadAll(resp.Body) - assert.NoError(t, err) - assert.JSONEq(t, tc.expectedBody, string(body)) - - // Verify that all expectations were met - mockClient.AssertExpectations(t) - }) - } -} - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - // Setup mock expectations - mockClient.On("Connect").Return(tc.connectError).Once() - if tc.connectError == nil { - mockClient.On("SubmitJob", mock.Anything).Return(tc.submitResponse, tc.submitError).Once() - mockClient.On("Close").Return().Once() - } - -// TestQueueEmpty tests the queueEmpty handler -func TestQueueEmpty(t *testing.T) { - // Setup test environment - _, mockClient, app := setupTest() - - // Test cases - tests := []struct { - name string - circleID string - topic string - connectError error - emptyError error - expectedStatus int - expectedBody string - }{ - { - name: "Success", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusOK, - expectedBody: `{"status":"success","message":"Queue for circle test-circle and topic test-topic emptied successfully"}`, - }, - { - name: "Connection Error", - circleID: "test-circle", - topic: "test-topic", - connectError: errors.New("connection error"), - emptyError: nil, - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to connect to HeroJobs server: connection error"}`, - }, - { - name: "Empty Error", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: errors.New("empty error"), - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to empty queue: empty error"}`, - }, - { - name: "Empty Circle ID", - circleID: "", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Circle ID is required"}`, - }, - { - name: "Empty Topic", - circleID: "test-circle", - topic: "", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Topic is required"}`, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - // Setup mock expectations - mockClient.On("Connect").Return(tc.connectError).Maybe() - if tc.connectError == nil && tc.circleID != "" && tc.topic != "" { - mockClient.On("QueueEmpty", tc.circleID, tc.topic).Return(tc.emptyError).Maybe() - mockClient.On("Close").Return(nil).Maybe() - } - - // Create request body - reqBody := map[string]string{ - "circleid": tc.circleID, - "topic": tc.topic, - } - reqBodyBytes, err := json.Marshal(reqBody) - assert.NoError(t, err) - - // Create test request - req, err := createTestRequest(http.MethodPost, "/api/jobs/queue/empty", bytes.NewReader(reqBodyBytes)) - assert.NoError(t, err) - req.Header.Set("Content-Type", "application/json") - - // Perform the request - resp, err := app.Test(req) - assert.NoError(t, err) - - // Check status code - assert.Equal(t, tc.expectedStatus, resp.StatusCode) - - // Check response body - body, err := io.ReadAll(resp.Body) - assert.NoError(t, err) - assert.JSONEq(t, tc.expectedBody, string(body)) - - // Verify that all expectations were met - mockClient.AssertExpectations(t) - }) - } -} - - // Create test request - req, err := createTestRequest(http.MethodPost, "/api/jobs/submit", testJob) - assert.NoError(t, err) - - // Perform the request - resp, err := app.Test(req) - assert.NoError(t, err) - - // Check status code - assert.Equal(t, tc.expectedStatus, resp.StatusCode) - - // Check response body - body, err := io.ReadAll(resp.Body) - assert.NoError(t, err) - assert.JSONEq(t, tc.expectedBody, string(body)) - - // Verify that all expectations were met - mockClient.AssertExpectations(t) - }) - } - -// TestQueueEmpty tests the queueEmpty handler -func TestQueueEmpty(t *testing.T) { - // Setup test environment - _, mockClient, app := setupTest() - - // Test cases - tests := []struct { - name string - circleID string - topic string - connectError error - emptyError error - expectedStatus int - expectedBody string - }{ - { - name: "Success", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusOK, - expectedBody: `{"status":"success","message":"Queue for circle test-circle and topic test-topic emptied successfully"}`, - }, - { - name: "Connection Error", - circleID: "test-circle", - topic: "test-topic", - connectError: errors.New("connection error"), - emptyError: nil, - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to connect to HeroJobs server: connection error"}`, - }, - { - name: "Empty Error", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: errors.New("empty error"), - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to empty queue: empty error"}`, - }, - { - name: "Empty Circle ID", - circleID: "", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Circle ID is required"}`, - }, - { - name: "Empty Topic", - circleID: "test-circle", - topic: "", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Topic is required"}`, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - // Setup mock expectations - mockClient.On("Connect").Return(tc.connectError).Maybe() - if tc.connectError == nil && tc.circleID != "" && tc.topic != "" { - mockClient.On("QueueEmpty", tc.circleID, tc.topic).Return(tc.emptyError).Maybe() - mockClient.On("Close").Return(nil).Maybe() - } - - // Create request body - reqBody := map[string]string{ - "circleid": tc.circleID, - "topic": tc.topic, - } - reqBodyBytes, err := json.Marshal(reqBody) - assert.NoError(t, err) - - // Create test request - req, err := createTestRequest(http.MethodPost, "/api/jobs/queue/empty", bytes.NewReader(reqBodyBytes)) - assert.NoError(t, err) - req.Header.Set("Content-Type", "application/json") - - // Perform the request - resp, err := app.Test(req) - assert.NoError(t, err) - - // Check status code - assert.Equal(t, tc.expectedStatus, resp.StatusCode) - - // Check response body - body, err := io.ReadAll(resp.Body) - assert.NoError(t, err) - assert.JSONEq(t, tc.expectedBody, string(body)) - - // Verify that all expectations were met - mockClient.AssertExpectations(t) - }) - } -} -} - -// TestQueueEmpty tests the queueEmpty handler -func TestQueueEmpty(t *testing.T) { - // Setup test environment - _, mockClient, app := setupTest() - - // Test cases - tests := []struct { - name string - circleID string - topic string - connectError error - emptyError error - expectedStatus int - expectedBody string - }{ - { - name: "Success", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusOK, - expectedBody: `{"status":"success","message":"Queue for circle test-circle and topic test-topic emptied successfully"}`, - }, - { - name: "Connection Error", - circleID: "test-circle", - topic: "test-topic", - connectError: errors.New("connection error"), - emptyError: nil, - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to connect to HeroJobs server: connection error"}`, - }, - { - name: "Empty Error", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: errors.New("empty error"), - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to empty queue: empty error"}`, - }, - { - name: "Empty Circle ID", - circleID: "", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Circle ID is required"}`, - }, - { - name: "Empty Topic", - circleID: "test-circle", - topic: "", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Topic is required"}`, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - // Setup mock expectations - mockClient.On("Connect").Return(tc.connectError).Maybe() - if tc.connectError == nil && tc.circleID != "" && tc.topic != "" { - mockClient.On("QueueEmpty", tc.circleID, tc.topic).Return(tc.emptyError).Maybe() - mockClient.On("Close").Return(nil).Maybe() - } - - // Create request body - reqBody := map[string]string{ - "circleid": tc.circleID, - "topic": tc.topic, - } - reqBodyBytes, err := json.Marshal(reqBody) - assert.NoError(t, err) - - // Create test request - req, err := createTestRequest(http.MethodPost, "/api/jobs/queue/empty", bytes.NewReader(reqBodyBytes)) - assert.NoError(t, err) - req.Header.Set("Content-Type", "application/json") - - // Perform the request - resp, err := app.Test(req) - assert.NoError(t, err) - - // Check status code - assert.Equal(t, tc.expectedStatus, resp.StatusCode) - - // Check response body - body, err := io.ReadAll(resp.Body) - assert.NoError(t, err) - assert.JSONEq(t, tc.expectedBody, string(body)) - - // Verify that all expectations were met - mockClient.AssertExpectations(t) - }) - } -} - -// TestGetJob tests the getJob handler -func TestGetJob(t *testing.T) { - // Setup test environment - _, mockClient, app := setupTest() - - // Create a test job - testJob := &herojobs.Job{ - JobID: "test-job-id", - CircleID: "test-circle", - Topic: "test-topic", - } - -// TestQueueEmpty tests the queueEmpty handler -func TestQueueEmpty(t *testing.T) { - // Setup test environment - _, mockClient, app := setupTest() - - // Test cases - tests := []struct { - name string - circleID string - topic string - connectError error - emptyError error - expectedStatus int - expectedBody string - }{ - { - name: "Success", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusOK, - expectedBody: `{"status":"success","message":"Queue for circle test-circle and topic test-topic emptied successfully"}`, - }, - { - name: "Connection Error", - circleID: "test-circle", - topic: "test-topic", - connectError: errors.New("connection error"), - emptyError: nil, - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to connect to HeroJobs server: connection error"}`, - }, - { - name: "Empty Error", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: errors.New("empty error"), - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to empty queue: empty error"}`, - }, - { - name: "Empty Circle ID", - circleID: "", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Circle ID is required"}`, - }, - { - name: "Empty Topic", - circleID: "test-circle", - topic: "", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Topic is required"}`, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - // Setup mock expectations - mockClient.On("Connect").Return(tc.connectError).Maybe() - if tc.connectError == nil && tc.circleID != "" && tc.topic != "" { - mockClient.On("QueueEmpty", tc.circleID, tc.topic).Return(tc.emptyError).Maybe() - mockClient.On("Close").Return(nil).Maybe() - } - - // Create request body - reqBody := map[string]string{ - "circleid": tc.circleID, - "topic": tc.topic, - } - reqBodyBytes, err := json.Marshal(reqBody) - assert.NoError(t, err) - - // Create test request - req, err := createTestRequest(http.MethodPost, "/api/jobs/queue/empty", bytes.NewReader(reqBodyBytes)) - assert.NoError(t, err) - req.Header.Set("Content-Type", "application/json") - - // Perform the request - resp, err := app.Test(req) - assert.NoError(t, err) - - // Check status code - assert.Equal(t, tc.expectedStatus, resp.StatusCode) - - // Check response body - body, err := io.ReadAll(resp.Body) - assert.NoError(t, err) - assert.JSONEq(t, tc.expectedBody, string(body)) - - // Verify that all expectations were met - mockClient.AssertExpectations(t) - }) - } -} - - // Test cases - tests := []struct { - name string - jobID string - connectError error - getError error - getResponse *herojobs.Job - expectedStatus int - expectedBody string - }{ - { - name: "Success", - jobID: "test-job-id", - connectError: nil, - getError: nil, - getResponse: testJob, - expectedStatus: fiber.StatusOK, - expectedBody: `{"jobid":"test-job-id","circleid":"test-circle","topic":"test-topic"}`, - }, - { - name: "Connection Error", - jobID: "test-job-id", - connectError: errors.New("connection error"), - getError: nil, - getResponse: nil, - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to connect to HeroJobs server: connection error"}`, - }, - { - name: "Get Error", - jobID: "test-job-id", - connectError: nil, - getError: errors.New("get error"), - getResponse: nil, - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to get job: get error"}`, - }, - { - name: "Empty Job ID", - jobID: "", - connectError: nil, - getError: nil, - getResponse: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Job ID is required"}`, - }, - } - -// TestQueueEmpty tests the queueEmpty handler -func TestQueueEmpty(t *testing.T) { - // Setup test environment - _, mockClient, app := setupTest() - - // Test cases - tests := []struct { - name string - circleID string - topic string - connectError error - emptyError error - expectedStatus int - expectedBody string - }{ - { - name: "Success", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusOK, - expectedBody: `{"status":"success","message":"Queue for circle test-circle and topic test-topic emptied successfully"}`, - }, - { - name: "Connection Error", - circleID: "test-circle", - topic: "test-topic", - connectError: errors.New("connection error"), - emptyError: nil, - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to connect to HeroJobs server: connection error"}`, - }, - { - name: "Empty Error", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: errors.New("empty error"), - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to empty queue: empty error"}`, - }, - { - name: "Empty Circle ID", - circleID: "", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Circle ID is required"}`, - }, - { - name: "Empty Topic", - circleID: "test-circle", - topic: "", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Topic is required"}`, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - // Setup mock expectations - mockClient.On("Connect").Return(tc.connectError).Maybe() - if tc.connectError == nil && tc.circleID != "" && tc.topic != "" { - mockClient.On("QueueEmpty", tc.circleID, tc.topic).Return(tc.emptyError).Maybe() - mockClient.On("Close").Return(nil).Maybe() - } - - // Create request body - reqBody := map[string]string{ - "circleid": tc.circleID, - "topic": tc.topic, - } - reqBodyBytes, err := json.Marshal(reqBody) - assert.NoError(t, err) - - // Create test request - req, err := createTestRequest(http.MethodPost, "/api/jobs/queue/empty", bytes.NewReader(reqBodyBytes)) - assert.NoError(t, err) - req.Header.Set("Content-Type", "application/json") - - // Perform the request - resp, err := app.Test(req) - assert.NoError(t, err) - - // Check status code - assert.Equal(t, tc.expectedStatus, resp.StatusCode) - - // Check response body - body, err := io.ReadAll(resp.Body) - assert.NoError(t, err) - assert.JSONEq(t, tc.expectedBody, string(body)) - - // Verify that all expectations were met - mockClient.AssertExpectations(t) - }) - } -} - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - // Setup mock expectations - mockClient.On("Connect").Return(tc.connectError).Maybe() - if tc.connectError == nil && tc.jobID != "" { - mockClient.On("GetJob", tc.jobID).Return(tc.getResponse, tc.getError).Maybe() - mockClient.On("Close").Return().Maybe() - } - -// TestQueueEmpty tests the queueEmpty handler -func TestQueueEmpty(t *testing.T) { - // Setup test environment - _, mockClient, app := setupTest() - - // Test cases - tests := []struct { - name string - circleID string - topic string - connectError error - emptyError error - expectedStatus int - expectedBody string - }{ - { - name: "Success", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusOK, - expectedBody: `{"status":"success","message":"Queue for circle test-circle and topic test-topic emptied successfully"}`, - }, - { - name: "Connection Error", - circleID: "test-circle", - topic: "test-topic", - connectError: errors.New("connection error"), - emptyError: nil, - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to connect to HeroJobs server: connection error"}`, - }, - { - name: "Empty Error", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: errors.New("empty error"), - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to empty queue: empty error"}`, - }, - { - name: "Empty Circle ID", - circleID: "", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Circle ID is required"}`, - }, - { - name: "Empty Topic", - circleID: "test-circle", - topic: "", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Topic is required"}`, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - // Setup mock expectations - mockClient.On("Connect").Return(tc.connectError).Maybe() - if tc.connectError == nil && tc.circleID != "" && tc.topic != "" { - mockClient.On("QueueEmpty", tc.circleID, tc.topic).Return(tc.emptyError).Maybe() - mockClient.On("Close").Return(nil).Maybe() - } - - // Create request body - reqBody := map[string]string{ - "circleid": tc.circleID, - "topic": tc.topic, - } - reqBodyBytes, err := json.Marshal(reqBody) - assert.NoError(t, err) - - // Create test request - req, err := createTestRequest(http.MethodPost, "/api/jobs/queue/empty", bytes.NewReader(reqBodyBytes)) - assert.NoError(t, err) - req.Header.Set("Content-Type", "application/json") - - // Perform the request - resp, err := app.Test(req) - assert.NoError(t, err) - - // Check status code - assert.Equal(t, tc.expectedStatus, resp.StatusCode) - - // Check response body - body, err := io.ReadAll(resp.Body) - assert.NoError(t, err) - assert.JSONEq(t, tc.expectedBody, string(body)) - - // Verify that all expectations were met - mockClient.AssertExpectations(t) - }) - } -} - - // Create test request - path := fmt.Sprintf("/api/jobs/get/%s", tc.jobID) - req, err := createTestRequest(http.MethodGet, path, nil) - assert.NoError(t, err) - - // Perform the request - resp, err := app.Test(req) - assert.NoError(t, err) - - // Check status code - assert.Equal(t, tc.expectedStatus, resp.StatusCode) - - // Check response body - body, err := io.ReadAll(resp.Body) - assert.NoError(t, err) - assert.JSONEq(t, tc.expectedBody, string(body)) - - // Verify that all expectations were met - mockClient.AssertExpectations(t) - }) - } - -// TestQueueEmpty tests the queueEmpty handler -func TestQueueEmpty(t *testing.T) { - // Setup test environment - _, mockClient, app := setupTest() - - // Test cases - tests := []struct { - name string - circleID string - topic string - connectError error - emptyError error - expectedStatus int - expectedBody string - }{ - { - name: "Success", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusOK, - expectedBody: `{"status":"success","message":"Queue for circle test-circle and topic test-topic emptied successfully"}`, - }, - { - name: "Connection Error", - circleID: "test-circle", - topic: "test-topic", - connectError: errors.New("connection error"), - emptyError: nil, - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to connect to HeroJobs server: connection error"}`, - }, - { - name: "Empty Error", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: errors.New("empty error"), - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to empty queue: empty error"}`, - }, - { - name: "Empty Circle ID", - circleID: "", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Circle ID is required"}`, - }, - { - name: "Empty Topic", - circleID: "test-circle", - topic: "", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Topic is required"}`, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - // Setup mock expectations - mockClient.On("Connect").Return(tc.connectError).Maybe() - if tc.connectError == nil && tc.circleID != "" && tc.topic != "" { - mockClient.On("QueueEmpty", tc.circleID, tc.topic).Return(tc.emptyError).Maybe() - mockClient.On("Close").Return(nil).Maybe() - } - - // Create request body - reqBody := map[string]string{ - "circleid": tc.circleID, - "topic": tc.topic, - } - reqBodyBytes, err := json.Marshal(reqBody) - assert.NoError(t, err) - - // Create test request - req, err := createTestRequest(http.MethodPost, "/api/jobs/queue/empty", bytes.NewReader(reqBodyBytes)) - assert.NoError(t, err) - req.Header.Set("Content-Type", "application/json") - - // Perform the request - resp, err := app.Test(req) - assert.NoError(t, err) - - // Check status code - assert.Equal(t, tc.expectedStatus, resp.StatusCode) - - // Check response body - body, err := io.ReadAll(resp.Body) - assert.NoError(t, err) - assert.JSONEq(t, tc.expectedBody, string(body)) - - // Verify that all expectations were met - mockClient.AssertExpectations(t) - }) - } -} -} - -// TestQueueEmpty tests the queueEmpty handler -func TestQueueEmpty(t *testing.T) { - // Setup test environment - _, mockClient, app := setupTest() - - // Test cases - tests := []struct { - name string - circleID string - topic string - connectError error - emptyError error - expectedStatus int - expectedBody string - }{ - { - name: "Success", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusOK, - expectedBody: `{"status":"success","message":"Queue for circle test-circle and topic test-topic emptied successfully"}`, - }, - { - name: "Connection Error", - circleID: "test-circle", - topic: "test-topic", - connectError: errors.New("connection error"), - emptyError: nil, - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to connect to HeroJobs server: connection error"}`, - }, - { - name: "Empty Error", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: errors.New("empty error"), - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to empty queue: empty error"}`, - }, - { - name: "Empty Circle ID", - circleID: "", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Circle ID is required"}`, - }, - { - name: "Empty Topic", - circleID: "test-circle", - topic: "", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Topic is required"}`, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - // Setup mock expectations - mockClient.On("Connect").Return(tc.connectError).Maybe() - if tc.connectError == nil && tc.circleID != "" && tc.topic != "" { - mockClient.On("QueueEmpty", tc.circleID, tc.topic).Return(tc.emptyError).Maybe() - mockClient.On("Close").Return(nil).Maybe() - } - - // Create request body - reqBody := map[string]string{ - "circleid": tc.circleID, - "topic": tc.topic, - } - reqBodyBytes, err := json.Marshal(reqBody) - assert.NoError(t, err) - - // Create test request - req, err := createTestRequest(http.MethodPost, "/api/jobs/queue/empty", bytes.NewReader(reqBodyBytes)) - assert.NoError(t, err) - req.Header.Set("Content-Type", "application/json") - - // Perform the request - resp, err := app.Test(req) - assert.NoError(t, err) - - // Check status code - assert.Equal(t, tc.expectedStatus, resp.StatusCode) - - // Check response body - body, err := io.ReadAll(resp.Body) - assert.NoError(t, err) - assert.JSONEq(t, tc.expectedBody, string(body)) - - // Verify that all expectations were met - mockClient.AssertExpectations(t) - }) - } -} - -// TestDeleteJob tests the deleteJob handler -func TestDeleteJob(t *testing.T) { - // Setup test environment - _, mockClient, app := setupTest() - - // Test cases - tests := []struct { - name string - jobID string - connectError error - deleteError error - expectedStatus int - expectedBody string - }{ - { - name: "Success", - jobID: "test-job-id", - connectError: nil, - deleteError: nil, - expectedStatus: fiber.StatusOK, - expectedBody: `{"status":"success","message":"Job test-job-id deleted successfully"}`, - }, - { - name: "Connection Error", - jobID: "test-job-id", - connectError: errors.New("connection error"), - deleteError: nil, - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to connect to HeroJobs server: connection error"}`, - }, - { - name: "Delete Error", - jobID: "test-job-id", - connectError: nil, - deleteError: errors.New("delete error"), - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to delete job: delete error"}`, - }, - { - name: "Empty Job ID", - jobID: "", - connectError: nil, - deleteError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Job ID is required"}`, - }, - } - -// TestQueueEmpty tests the queueEmpty handler -func TestQueueEmpty(t *testing.T) { - // Setup test environment - _, mockClient, app := setupTest() - - // Test cases - tests := []struct { - name string - circleID string - topic string - connectError error - emptyError error - expectedStatus int - expectedBody string - }{ - { - name: "Success", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusOK, - expectedBody: `{"status":"success","message":"Queue for circle test-circle and topic test-topic emptied successfully"}`, - }, - { - name: "Connection Error", - circleID: "test-circle", - topic: "test-topic", - connectError: errors.New("connection error"), - emptyError: nil, - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to connect to HeroJobs server: connection error"}`, - }, - { - name: "Empty Error", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: errors.New("empty error"), - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to empty queue: empty error"}`, - }, - { - name: "Empty Circle ID", - circleID: "", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Circle ID is required"}`, - }, - { - name: "Empty Topic", - circleID: "test-circle", - topic: "", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Topic is required"}`, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - // Setup mock expectations - mockClient.On("Connect").Return(tc.connectError).Maybe() - if tc.connectError == nil && tc.circleID != "" && tc.topic != "" { - mockClient.On("QueueEmpty", tc.circleID, tc.topic).Return(tc.emptyError).Maybe() - mockClient.On("Close").Return(nil).Maybe() - } - - // Create request body - reqBody := map[string]string{ - "circleid": tc.circleID, - "topic": tc.topic, - } - reqBodyBytes, err := json.Marshal(reqBody) - assert.NoError(t, err) - - // Create test request - req, err := createTestRequest(http.MethodPost, "/api/jobs/queue/empty", bytes.NewReader(reqBodyBytes)) - assert.NoError(t, err) - req.Header.Set("Content-Type", "application/json") - - // Perform the request - resp, err := app.Test(req) - assert.NoError(t, err) - - // Check status code - assert.Equal(t, tc.expectedStatus, resp.StatusCode) - - // Check response body - body, err := io.ReadAll(resp.Body) - assert.NoError(t, err) - assert.JSONEq(t, tc.expectedBody, string(body)) - - // Verify that all expectations were met - mockClient.AssertExpectations(t) - }) - } -} - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - // Setup mock expectations - mockClient.On("Connect").Return(tc.connectError).Maybe() - if tc.connectError == nil && tc.jobID != "" { - mockClient.On("DeleteJob", tc.jobID).Return(tc.deleteError).Maybe() - mockClient.On("Close").Return().Maybe() - } - -// TestQueueEmpty tests the queueEmpty handler -func TestQueueEmpty(t *testing.T) { - // Setup test environment - _, mockClient, app := setupTest() - - // Test cases - tests := []struct { - name string - circleID string - topic string - connectError error - emptyError error - expectedStatus int - expectedBody string - }{ - { - name: "Success", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusOK, - expectedBody: `{"status":"success","message":"Queue for circle test-circle and topic test-topic emptied successfully"}`, - }, - { - name: "Connection Error", - circleID: "test-circle", - topic: "test-topic", - connectError: errors.New("connection error"), - emptyError: nil, - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to connect to HeroJobs server: connection error"}`, - }, - { - name: "Empty Error", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: errors.New("empty error"), - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to empty queue: empty error"}`, - }, - { - name: "Empty Circle ID", - circleID: "", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Circle ID is required"}`, - }, - { - name: "Empty Topic", - circleID: "test-circle", - topic: "", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Topic is required"}`, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - // Setup mock expectations - mockClient.On("Connect").Return(tc.connectError).Maybe() - if tc.connectError == nil && tc.circleID != "" && tc.topic != "" { - mockClient.On("QueueEmpty", tc.circleID, tc.topic).Return(tc.emptyError).Maybe() - mockClient.On("Close").Return(nil).Maybe() - } - - // Create request body - reqBody := map[string]string{ - "circleid": tc.circleID, - "topic": tc.topic, - } - reqBodyBytes, err := json.Marshal(reqBody) - assert.NoError(t, err) - - // Create test request - req, err := createTestRequest(http.MethodPost, "/api/jobs/queue/empty", bytes.NewReader(reqBodyBytes)) - assert.NoError(t, err) - req.Header.Set("Content-Type", "application/json") - - // Perform the request - resp, err := app.Test(req) - assert.NoError(t, err) - - // Check status code - assert.Equal(t, tc.expectedStatus, resp.StatusCode) - - // Check response body - body, err := io.ReadAll(resp.Body) - assert.NoError(t, err) - assert.JSONEq(t, tc.expectedBody, string(body)) - - // Verify that all expectations were met - mockClient.AssertExpectations(t) - }) - } -} - - // Create test request - path := fmt.Sprintf("/api/jobs/delete/%s", tc.jobID) - req, err := createTestRequest(http.MethodDelete, path, nil) - assert.NoError(t, err) - - // Perform the request - resp, err := app.Test(req) - assert.NoError(t, err) - - // Check status code - assert.Equal(t, tc.expectedStatus, resp.StatusCode) - - // Check response body - body, err := io.ReadAll(resp.Body) - assert.NoError(t, err) - assert.JSONEq(t, tc.expectedBody, string(body)) - - // Verify that all expectations were met - mockClient.AssertExpectations(t) - }) - } - -// TestQueueEmpty tests the queueEmpty handler -func TestQueueEmpty(t *testing.T) { - // Setup test environment - _, mockClient, app := setupTest() - - // Test cases - tests := []struct { - name string - circleID string - topic string - connectError error - emptyError error - expectedStatus int - expectedBody string - }{ - { - name: "Success", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusOK, - expectedBody: `{"status":"success","message":"Queue for circle test-circle and topic test-topic emptied successfully"}`, - }, - { - name: "Connection Error", - circleID: "test-circle", - topic: "test-topic", - connectError: errors.New("connection error"), - emptyError: nil, - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to connect to HeroJobs server: connection error"}`, - }, - { - name: "Empty Error", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: errors.New("empty error"), - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to empty queue: empty error"}`, - }, - { - name: "Empty Circle ID", - circleID: "", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Circle ID is required"}`, - }, - { - name: "Empty Topic", - circleID: "test-circle", - topic: "", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Topic is required"}`, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - // Setup mock expectations - mockClient.On("Connect").Return(tc.connectError).Maybe() - if tc.connectError == nil && tc.circleID != "" && tc.topic != "" { - mockClient.On("QueueEmpty", tc.circleID, tc.topic).Return(tc.emptyError).Maybe() - mockClient.On("Close").Return(nil).Maybe() - } - - // Create request body - reqBody := map[string]string{ - "circleid": tc.circleID, - "topic": tc.topic, - } - reqBodyBytes, err := json.Marshal(reqBody) - assert.NoError(t, err) - - // Create test request - req, err := createTestRequest(http.MethodPost, "/api/jobs/queue/empty", bytes.NewReader(reqBodyBytes)) - assert.NoError(t, err) - req.Header.Set("Content-Type", "application/json") - - // Perform the request - resp, err := app.Test(req) - assert.NoError(t, err) - - // Check status code - assert.Equal(t, tc.expectedStatus, resp.StatusCode) - - // Check response body - body, err := io.ReadAll(resp.Body) - assert.NoError(t, err) - assert.JSONEq(t, tc.expectedBody, string(body)) - - // Verify that all expectations were met - mockClient.AssertExpectations(t) - }) - } -} -} - -// TestQueueEmpty tests the queueEmpty handler -func TestQueueEmpty(t *testing.T) { - // Setup test environment - _, mockClient, app := setupTest() - - // Test cases - tests := []struct { - name string - circleID string - topic string - connectError error - emptyError error - expectedStatus int - expectedBody string - }{ - { - name: "Success", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusOK, - expectedBody: `{"status":"success","message":"Queue for circle test-circle and topic test-topic emptied successfully"}`, - }, - { - name: "Connection Error", - circleID: "test-circle", - topic: "test-topic", - connectError: errors.New("connection error"), - emptyError: nil, - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to connect to HeroJobs server: connection error"}`, - }, - { - name: "Empty Error", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: errors.New("empty error"), - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to empty queue: empty error"}`, - }, - { - name: "Empty Circle ID", - circleID: "", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Circle ID is required"}`, - }, - { - name: "Empty Topic", - circleID: "test-circle", - topic: "", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Topic is required"}`, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - // Setup mock expectations - mockClient.On("Connect").Return(tc.connectError).Maybe() - if tc.connectError == nil && tc.circleID != "" && tc.topic != "" { - mockClient.On("QueueEmpty", tc.circleID, tc.topic).Return(tc.emptyError).Maybe() - mockClient.On("Close").Return(nil).Maybe() - } - - // Create request body - reqBody := map[string]string{ - "circleid": tc.circleID, - "topic": tc.topic, - } - reqBodyBytes, err := json.Marshal(reqBody) - assert.NoError(t, err) - - // Create test request - req, err := createTestRequest(http.MethodPost, "/api/jobs/queue/empty", bytes.NewReader(reqBodyBytes)) - assert.NoError(t, err) - req.Header.Set("Content-Type", "application/json") - - // Perform the request - resp, err := app.Test(req) - assert.NoError(t, err) - - // Check status code - assert.Equal(t, tc.expectedStatus, resp.StatusCode) - - // Check response body - body, err := io.ReadAll(resp.Body) - assert.NoError(t, err) - assert.JSONEq(t, tc.expectedBody, string(body)) - - // Verify that all expectations were met - mockClient.AssertExpectations(t) - }) - } -} - -// TestListJobs tests the listJobs handler -func TestListJobs(t *testing.T) { - // Setup test environment - _, mockClient, app := setupTest() - - // Test cases - tests := []struct { - name string - circleID string - topic string - connectError error - listError error - listResponse []string - expectedStatus int - expectedBody string - }{ - { - name: "Success", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - listError: nil, - listResponse: []string{"job1", "job2", "job3"}, - expectedStatus: fiber.StatusOK, - expectedBody: `{"jobs":["job1","job2","job3"]}`, - }, - { - name: "Connection Error", - circleID: "test-circle", - topic: "test-topic", - connectError: errors.New("connection error"), - listError: nil, - listResponse: nil, - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to connect to HeroJobs server: connection error"}`, - }, - { - name: "List Error", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - listError: errors.New("list error"), - listResponse: nil, - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to list jobs: list error"}`, - }, - { - name: "Empty Circle ID", - circleID: "", - topic: "test-topic", - connectError: nil, - listError: nil, - listResponse: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Circle ID is required"}`, - }, - { - name: "Empty Topic", - circleID: "test-circle", - topic: "", - connectError: nil, - listError: nil, - listResponse: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Topic is required"}`, - }, - } - -// TestQueueEmpty tests the queueEmpty handler -func TestQueueEmpty(t *testing.T) { - // Setup test environment - _, mockClient, app := setupTest() - - // Test cases - tests := []struct { - name string - circleID string - topic string - connectError error - emptyError error - expectedStatus int - expectedBody string - }{ - { - name: "Success", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusOK, - expectedBody: `{"status":"success","message":"Queue for circle test-circle and topic test-topic emptied successfully"}`, - }, - { - name: "Connection Error", - circleID: "test-circle", - topic: "test-topic", - connectError: errors.New("connection error"), - emptyError: nil, - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to connect to HeroJobs server: connection error"}`, - }, - { - name: "Empty Error", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: errors.New("empty error"), - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to empty queue: empty error"}`, - }, - { - name: "Empty Circle ID", - circleID: "", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Circle ID is required"}`, - }, - { - name: "Empty Topic", - circleID: "test-circle", - topic: "", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Topic is required"}`, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - // Setup mock expectations - mockClient.On("Connect").Return(tc.connectError).Maybe() - if tc.connectError == nil && tc.circleID != "" && tc.topic != "" { - mockClient.On("QueueEmpty", tc.circleID, tc.topic).Return(tc.emptyError).Maybe() - mockClient.On("Close").Return(nil).Maybe() - } - - // Create request body - reqBody := map[string]string{ - "circleid": tc.circleID, - "topic": tc.topic, - } - reqBodyBytes, err := json.Marshal(reqBody) - assert.NoError(t, err) - - // Create test request - req, err := createTestRequest(http.MethodPost, "/api/jobs/queue/empty", bytes.NewReader(reqBodyBytes)) - assert.NoError(t, err) - req.Header.Set("Content-Type", "application/json") - - // Perform the request - resp, err := app.Test(req) - assert.NoError(t, err) - - // Check status code - assert.Equal(t, tc.expectedStatus, resp.StatusCode) - - // Check response body - body, err := io.ReadAll(resp.Body) - assert.NoError(t, err) - assert.JSONEq(t, tc.expectedBody, string(body)) - - // Verify that all expectations were met - mockClient.AssertExpectations(t) - }) - } -} - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - // Setup mock expectations - mockClient.On("Connect").Return(tc.connectError).Maybe() - if tc.connectError == nil && tc.circleID != "" && tc.topic != "" { - mockClient.On("ListJobs", tc.circleID, tc.topic).Return(tc.listResponse, tc.listError).Maybe() - mockClient.On("Close").Return().Maybe() - } - -// TestQueueEmpty tests the queueEmpty handler -func TestQueueEmpty(t *testing.T) { - // Setup test environment - _, mockClient, app := setupTest() - - // Test cases - tests := []struct { - name string - circleID string - topic string - connectError error - emptyError error - expectedStatus int - expectedBody string - }{ - { - name: "Success", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusOK, - expectedBody: `{"status":"success","message":"Queue for circle test-circle and topic test-topic emptied successfully"}`, - }, - { - name: "Connection Error", - circleID: "test-circle", - topic: "test-topic", - connectError: errors.New("connection error"), - emptyError: nil, - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to connect to HeroJobs server: connection error"}`, - }, - { - name: "Empty Error", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: errors.New("empty error"), - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to empty queue: empty error"}`, - }, - { - name: "Empty Circle ID", - circleID: "", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Circle ID is required"}`, - }, - { - name: "Empty Topic", - circleID: "test-circle", - topic: "", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Topic is required"}`, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - // Setup mock expectations - mockClient.On("Connect").Return(tc.connectError).Maybe() - if tc.connectError == nil && tc.circleID != "" && tc.topic != "" { - mockClient.On("QueueEmpty", tc.circleID, tc.topic).Return(tc.emptyError).Maybe() - mockClient.On("Close").Return(nil).Maybe() - } - - // Create request body - reqBody := map[string]string{ - "circleid": tc.circleID, - "topic": tc.topic, - } - reqBodyBytes, err := json.Marshal(reqBody) - assert.NoError(t, err) - - // Create test request - req, err := createTestRequest(http.MethodPost, "/api/jobs/queue/empty", bytes.NewReader(reqBodyBytes)) - assert.NoError(t, err) - req.Header.Set("Content-Type", "application/json") - - // Perform the request - resp, err := app.Test(req) - assert.NoError(t, err) - - // Check status code - assert.Equal(t, tc.expectedStatus, resp.StatusCode) - - // Check response body - body, err := io.ReadAll(resp.Body) - assert.NoError(t, err) - assert.JSONEq(t, tc.expectedBody, string(body)) - - // Verify that all expectations were met - mockClient.AssertExpectations(t) - }) - } -} - - // Create test request - path := fmt.Sprintf("/api/jobs/list?circleid=%s&topic=%s", tc.circleID, tc.topic) - req, err := createTestRequest(http.MethodGet, path, nil) - assert.NoError(t, err) - - // Perform the request - resp, err := app.Test(req) - assert.NoError(t, err) - - // Check status code - assert.Equal(t, tc.expectedStatus, resp.StatusCode) - - // Check response body - body, err := io.ReadAll(resp.Body) - assert.NoError(t, err) - assert.JSONEq(t, tc.expectedBody, string(body)) - - // Verify that all expectations were met - mockClient.AssertExpectations(t) - }) - } - -// TestQueueEmpty tests the queueEmpty handler -func TestQueueEmpty(t *testing.T) { - // Setup test environment - _, mockClient, app := setupTest() - - // Test cases - tests := []struct { - name string - circleID string - topic string - connectError error - emptyError error - expectedStatus int - expectedBody string - }{ - { - name: "Success", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusOK, - expectedBody: `{"status":"success","message":"Queue for circle test-circle and topic test-topic emptied successfully"}`, - }, - { - name: "Connection Error", - circleID: "test-circle", - topic: "test-topic", - connectError: errors.New("connection error"), - emptyError: nil, - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to connect to HeroJobs server: connection error"}`, - }, - { - name: "Empty Error", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: errors.New("empty error"), - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to empty queue: empty error"}`, - }, - { - name: "Empty Circle ID", - circleID: "", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Circle ID is required"}`, - }, - { - name: "Empty Topic", - circleID: "test-circle", - topic: "", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Topic is required"}`, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - // Setup mock expectations - mockClient.On("Connect").Return(tc.connectError).Maybe() - if tc.connectError == nil && tc.circleID != "" && tc.topic != "" { - mockClient.On("QueueEmpty", tc.circleID, tc.topic).Return(tc.emptyError).Maybe() - mockClient.On("Close").Return(nil).Maybe() - } - - // Create request body - reqBody := map[string]string{ - "circleid": tc.circleID, - "topic": tc.topic, - } - reqBodyBytes, err := json.Marshal(reqBody) - assert.NoError(t, err) - - // Create test request - req, err := createTestRequest(http.MethodPost, "/api/jobs/queue/empty", bytes.NewReader(reqBodyBytes)) - assert.NoError(t, err) - req.Header.Set("Content-Type", "application/json") - - // Perform the request - resp, err := app.Test(req) - assert.NoError(t, err) - - // Check status code - assert.Equal(t, tc.expectedStatus, resp.StatusCode) - - // Check response body - body, err := io.ReadAll(resp.Body) - assert.NoError(t, err) - assert.JSONEq(t, tc.expectedBody, string(body)) - - // Verify that all expectations were met - mockClient.AssertExpectations(t) - }) - } -} -} - -// TestQueueEmpty tests the queueEmpty handler -func TestQueueEmpty(t *testing.T) { - // Setup test environment - _, mockClient, app := setupTest() - - // Test cases - tests := []struct { - name string - circleID string - topic string - connectError error - emptyError error - expectedStatus int - expectedBody string - }{ - { - name: "Success", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusOK, - expectedBody: `{"status":"success","message":"Queue for circle test-circle and topic test-topic emptied successfully"}`, - }, - { - name: "Connection Error", - circleID: "test-circle", - topic: "test-topic", - connectError: errors.New("connection error"), - emptyError: nil, - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to connect to HeroJobs server: connection error"}`, - }, - { - name: "Empty Error", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: errors.New("empty error"), - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to empty queue: empty error"}`, - }, - { - name: "Empty Circle ID", - circleID: "", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Circle ID is required"}`, - }, - { - name: "Empty Topic", - circleID: "test-circle", - topic: "", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Topic is required"}`, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - // Setup mock expectations - mockClient.On("Connect").Return(tc.connectError).Maybe() - if tc.connectError == nil && tc.circleID != "" && tc.topic != "" { - mockClient.On("QueueEmpty", tc.circleID, tc.topic).Return(tc.emptyError).Maybe() - mockClient.On("Close").Return(nil).Maybe() - } - - // Create request body - reqBody := map[string]string{ - "circleid": tc.circleID, - "topic": tc.topic, - } - reqBodyBytes, err := json.Marshal(reqBody) - assert.NoError(t, err) - - // Create test request - req, err := createTestRequest(http.MethodPost, "/api/jobs/queue/empty", bytes.NewReader(reqBodyBytes)) - assert.NoError(t, err) - req.Header.Set("Content-Type", "application/json") - - // Perform the request - resp, err := app.Test(req) - assert.NoError(t, err) - - // Check status code - assert.Equal(t, tc.expectedStatus, resp.StatusCode) - - // Check response body - body, err := io.ReadAll(resp.Body) - assert.NoError(t, err) - assert.JSONEq(t, tc.expectedBody, string(body)) - - // Verify that all expectations were met - mockClient.AssertExpectations(t) - }) - } -} - -// TestQueueSize tests the queueSize handler -func TestQueueSize(t *testing.T) { - // Setup test environment - _, mockClient, app := setupTest() - - // Test cases - tests := []struct { - name string - circleID string - topic string - connectError error - sizeError error - sizeResponse int64 - expectedStatus int - expectedBody string - }{ - { - name: "Success", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - sizeError: nil, - sizeResponse: 42, - expectedStatus: fiber.StatusOK, - expectedBody: `{"size":42}`, - }, - { - name: "Connection Error", - circleID: "test-circle", - topic: "test-topic", - connectError: errors.New("connection error"), - sizeError: nil, - sizeResponse: 0, - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to connect to HeroJobs server: connection error"}`, - }, - { - name: "Size Error", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - sizeError: errors.New("size error"), - sizeResponse: 0, - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to get queue size: size error"}`, - }, - { - name: "Empty Circle ID", - circleID: "", - topic: "test-topic", - connectError: nil, - sizeError: nil, - sizeResponse: 0, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Circle ID is required"}`, - }, - { - name: "Empty Topic", - circleID: "test-circle", - topic: "", - connectError: nil, - sizeError: nil, - sizeResponse: 0, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Topic is required"}`, - }, - } - -// TestQueueEmpty tests the queueEmpty handler -func TestQueueEmpty(t *testing.T) { - // Setup test environment - _, mockClient, app := setupTest() - - // Test cases - tests := []struct { - name string - circleID string - topic string - connectError error - emptyError error - expectedStatus int - expectedBody string - }{ - { - name: "Success", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusOK, - expectedBody: `{"status":"success","message":"Queue for circle test-circle and topic test-topic emptied successfully"}`, - }, - { - name: "Connection Error", - circleID: "test-circle", - topic: "test-topic", - connectError: errors.New("connection error"), - emptyError: nil, - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to connect to HeroJobs server: connection error"}`, - }, - { - name: "Empty Error", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: errors.New("empty error"), - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to empty queue: empty error"}`, - }, - { - name: "Empty Circle ID", - circleID: "", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Circle ID is required"}`, - }, - { - name: "Empty Topic", - circleID: "test-circle", - topic: "", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Topic is required"}`, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - // Setup mock expectations - mockClient.On("Connect").Return(tc.connectError).Maybe() - if tc.connectError == nil && tc.circleID != "" && tc.topic != "" { - mockClient.On("QueueEmpty", tc.circleID, tc.topic).Return(tc.emptyError).Maybe() - mockClient.On("Close").Return(nil).Maybe() - } - - // Create request body - reqBody := map[string]string{ - "circleid": tc.circleID, - "topic": tc.topic, - } - reqBodyBytes, err := json.Marshal(reqBody) - assert.NoError(t, err) - - // Create test request - req, err := createTestRequest(http.MethodPost, "/api/jobs/queue/empty", bytes.NewReader(reqBodyBytes)) - assert.NoError(t, err) - req.Header.Set("Content-Type", "application/json") - - // Perform the request - resp, err := app.Test(req) - assert.NoError(t, err) - - // Check status code - assert.Equal(t, tc.expectedStatus, resp.StatusCode) - - // Check response body - body, err := io.ReadAll(resp.Body) - assert.NoError(t, err) - assert.JSONEq(t, tc.expectedBody, string(body)) - - // Verify that all expectations were met - mockClient.AssertExpectations(t) - }) - } -} - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - // Setup mock expectations - mockClient.On("Connect").Return(tc.connectError).Maybe() - if tc.connectError == nil && tc.circleID != "" && tc.topic != "" { - mockClient.On("QueueSize", tc.circleID, tc.topic).Return(tc.sizeResponse, tc.sizeError).Maybe() - mockClient.On("Close").Return().Maybe() - } - -// TestQueueEmpty tests the queueEmpty handler -func TestQueueEmpty(t *testing.T) { - // Setup test environment - _, mockClient, app := setupTest() - - // Test cases - tests := []struct { - name string - circleID string - topic string - connectError error - emptyError error - expectedStatus int - expectedBody string - }{ - { - name: "Success", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusOK, - expectedBody: `{"status":"success","message":"Queue for circle test-circle and topic test-topic emptied successfully"}`, - }, - { - name: "Connection Error", - circleID: "test-circle", - topic: "test-topic", - connectError: errors.New("connection error"), - emptyError: nil, - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to connect to HeroJobs server: connection error"}`, - }, - { - name: "Empty Error", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: errors.New("empty error"), - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to empty queue: empty error"}`, - }, - { - name: "Empty Circle ID", - circleID: "", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Circle ID is required"}`, - }, - { - name: "Empty Topic", - circleID: "test-circle", - topic: "", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Topic is required"}`, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - // Setup mock expectations - mockClient.On("Connect").Return(tc.connectError).Maybe() - if tc.connectError == nil && tc.circleID != "" && tc.topic != "" { - mockClient.On("QueueEmpty", tc.circleID, tc.topic).Return(tc.emptyError).Maybe() - mockClient.On("Close").Return(nil).Maybe() - } - - // Create request body - reqBody := map[string]string{ - "circleid": tc.circleID, - "topic": tc.topic, - } - reqBodyBytes, err := json.Marshal(reqBody) - assert.NoError(t, err) - - // Create test request - req, err := createTestRequest(http.MethodPost, "/api/jobs/queue/empty", bytes.NewReader(reqBodyBytes)) - assert.NoError(t, err) - req.Header.Set("Content-Type", "application/json") - - // Perform the request - resp, err := app.Test(req) - assert.NoError(t, err) - - // Check status code - assert.Equal(t, tc.expectedStatus, resp.StatusCode) - - // Check response body - body, err := io.ReadAll(resp.Body) - assert.NoError(t, err) - assert.JSONEq(t, tc.expectedBody, string(body)) - - // Verify that all expectations were met - mockClient.AssertExpectations(t) - }) - } -} - - // Create test request - path := fmt.Sprintf("/api/jobs/queue/size?circleid=%s&topic=%s", tc.circleID, tc.topic) - req, err := createTestRequest(http.MethodGet, path, nil) - assert.NoError(t, err) - - // Perform the request - resp, err := app.Test(req) - assert.NoError(t, err) - - // Check status code - assert.Equal(t, tc.expectedStatus, resp.StatusCode) - - // Check response body - body, err := io.ReadAll(resp.Body) - assert.NoError(t, err) - assert.JSONEq(t, tc.expectedBody, string(body)) - - // Verify that all expectations were met - mockClient.AssertExpectations(t) - }) - } - -// TestQueueEmpty tests the queueEmpty handler -func TestQueueEmpty(t *testing.T) { - // Setup test environment - _, mockClient, app := setupTest() - - // Test cases - tests := []struct { - name string - circleID string - topic string - connectError error - emptyError error - expectedStatus int - expectedBody string - }{ - { - name: "Success", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusOK, - expectedBody: `{"status":"success","message":"Queue for circle test-circle and topic test-topic emptied successfully"}`, - }, - { - name: "Connection Error", - circleID: "test-circle", - topic: "test-topic", - connectError: errors.New("connection error"), - emptyError: nil, - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to connect to HeroJobs server: connection error"}`, - }, - { - name: "Empty Error", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: errors.New("empty error"), - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to empty queue: empty error"}`, - }, - { - name: "Empty Circle ID", - circleID: "", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Circle ID is required"}`, - }, - { - name: "Empty Topic", - circleID: "test-circle", - topic: "", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Topic is required"}`, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - // Setup mock expectations - mockClient.On("Connect").Return(tc.connectError).Maybe() - if tc.connectError == nil && tc.circleID != "" && tc.topic != "" { - mockClient.On("QueueEmpty", tc.circleID, tc.topic).Return(tc.emptyError).Maybe() - mockClient.On("Close").Return(nil).Maybe() - } - - // Create request body - reqBody := map[string]string{ - "circleid": tc.circleID, - "topic": tc.topic, - } - reqBodyBytes, err := json.Marshal(reqBody) - assert.NoError(t, err) - - // Create test request - req, err := createTestRequest(http.MethodPost, "/api/jobs/queue/empty", bytes.NewReader(reqBodyBytes)) - assert.NoError(t, err) - req.Header.Set("Content-Type", "application/json") - - // Perform the request - resp, err := app.Test(req) - assert.NoError(t, err) - - // Check status code - assert.Equal(t, tc.expectedStatus, resp.StatusCode) - - // Check response body - body, err := io.ReadAll(resp.Body) - assert.NoError(t, err) - assert.JSONEq(t, tc.expectedBody, string(body)) - - // Verify that all expectations were met - mockClient.AssertExpectations(t) - }) - } -} -} - -// TestQueueEmpty tests the queueEmpty handler -func TestQueueEmpty(t *testing.T) { - // Setup test environment - _, mockClient, app := setupTest() - - // Test cases - tests := []struct { - name string - circleID string - topic string - connectError error - emptyError error - expectedStatus int - expectedBody string - }{ - { - name: "Success", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusOK, - expectedBody: `{"status":"success","message":"Queue for circle test-circle and topic test-topic emptied successfully"}`, - }, - { - name: "Connection Error", - circleID: "test-circle", - topic: "test-topic", - connectError: errors.New("connection error"), - emptyError: nil, - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to connect to HeroJobs server: connection error"}`, - }, - { - name: "Empty Error", - circleID: "test-circle", - topic: "test-topic", - connectError: nil, - emptyError: errors.New("empty error"), - expectedStatus: fiber.StatusInternalServerError, - expectedBody: `{"error":"Failed to empty queue: empty error"}`, - }, - { - name: "Empty Circle ID", - circleID: "", - topic: "test-topic", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Circle ID is required"}`, - }, - { - name: "Empty Topic", - circleID: "test-circle", - topic: "", - connectError: nil, - emptyError: nil, - expectedStatus: fiber.StatusBadRequest, - expectedBody: `{"error":"Topic is required"}`, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - // Setup mock expectations - mockClient.On("Connect").Return(tc.connectError).Maybe() - if tc.connectError == nil && tc.circleID != "" && tc.topic != "" { - mockClient.On("QueueEmpty", tc.circleID, tc.topic).Return(tc.emptyError).Maybe() - mockClient.On("Close").Return(nil).Maybe() - } - - // Create request body - reqBody := map[string]string{ - "circleid": tc.circleID, - "topic": tc.topic, - } - reqBodyBytes, err := json.Marshal(reqBody) - assert.NoError(t, err) - - // Create test request - req, err := createTestRequest(http.MethodPost, "/api/jobs/queue/empty", bytes.NewReader(reqBodyBytes)) - assert.NoError(t, err) - req.Header.Set("Content-Type", "application/json") - - // Perform the request - resp, err := app.Test(req) - assert.NoError(t, err) - - // Check status code - assert.Equal(t, tc.expectedStatus, resp.StatusCode) - - // Check response body - body, err := io.ReadAll(resp.Body) - assert.NoError(t, err) - assert.JSONEq(t, tc.expectedBody, string(body)) - - // Verify that all expectations were met - mockClient.AssertExpectations(t) - }) - } -} diff --git a/pkg/heroagent/pages/jobs.go b/pkg/heroagent/pages/jobs.go index 018adf2..26f91ec 100644 --- a/pkg/heroagent/pages/jobs.go +++ b/pkg/heroagent/pages/jobs.go @@ -15,8 +15,8 @@ type JobDisplayInfo struct { Topic string `json:"topic"` Status string `json:"status"` SessionKey string `json:"sessionkey"` - HeroScript string `json:"heroscript"` - RhaiScript string `json:"rhaiscript"` + Params string `json:"params"` + ParamsType string `json:"paramstype"` Result string `json:"result"` Error string `json:"error"` TimeScheduled int64 `json:"time_scheduled"` @@ -27,15 +27,17 @@ type JobDisplayInfo struct { // JobHandler handles job-related page routes type JobHandler struct { - client *herojobs.Client + client *herojobs.RedisClient logger *log.Logger } // NewJobHandler creates a new job handler with the provided socket path -func NewJobHandler(socketPath string, logger *log.Logger) (*JobHandler, error) { - client, err := herojobs.NewClient(socketPath) +func NewJobHandler(redisAddr string, logger *log.Logger) (*JobHandler, error) { + // Assuming SSL is false as per README example herojobs.NewRedisClient("localhost:6379", false) + // This might need to be configurable later. + client, err := herojobs.NewRedisClient(redisAddr, false) if err != nil { - return nil, fmt.Errorf("failed to create HeroJobs client: %w", err) + return nil, fmt.Errorf("failed to create HeroJobs Redis client: %w", err) } return &JobHandler{ @@ -50,7 +52,7 @@ func (h *JobHandler) RegisterRoutes(app *fiber.App) { jobs := app.Group("/jobs") jobs.Get("/", h.getJobsPage) jobs.Get("/list", h.getJobsList) - + // Register the same routes under /admin/jobs for consistency adminJobs := app.Group("/admin/jobs") adminJobs.Get("/", h.getJobsPage) @@ -59,18 +61,13 @@ func (h *JobHandler) RegisterRoutes(app *fiber.App) { // getJobsPage renders the jobs page func (h *JobHandler) getJobsPage(c *fiber.Ctx) error { - // Check if we can connect to the HeroJobs server - var warning string - if err := h.client.Connect(); err != nil { - warning = "Could not connect to HeroJobs server: " + err.Error() - h.logger.Printf("Warning: %s", warning) - } else { - h.client.Close() - } - + // Assuming h.client (RedisClient) is valid if NewJobHandler succeeded. + // The client is connected on creation. A Ping method could be used here for a health check if available. + // The previous connect/close logic per-request is removed. + var warning string // This will be empty unless a new check (e.g., Ping) sets it. return c.Render("admin/jobs", fiber.Map{ "title": "Jobs", - "warning": warning, + "warning": warning, // warning will be empty for now "error": "", }) } @@ -100,20 +97,18 @@ func (h *JobHandler) getJobsList(c *fiber.Ctx) error { // getJobsData gets job data from the HeroJobs server func (h *JobHandler) getJobsData(circleID, topic string) ([]JobDisplayInfo, error) { - // Connect to the HeroJobs server - if err := h.client.Connect(); err != nil { - return nil, fmt.Errorf("failed to connect to HeroJobs server: %w", err) - } - defer h.client.Close() + // Assuming h.client (RedisClient) is already connected (established by NewJobHandler). + // It should not be closed here as it's a long-lived client. + // Connect() and Close() calls per-request are removed. // If circleID and topic are not provided, try to list all jobs if circleID == "" && topic == "" { // Try to get some default jobs defaultCircles := []string{"default", "system"} defaultTopics := []string{"default", "system"} - + var allJobs []JobDisplayInfo - + // Try each combination for _, circle := range defaultCircles { for _, t := range defaultTopics { @@ -122,22 +117,22 @@ func (h *JobHandler) getJobsData(circleID, topic string) ([]JobDisplayInfo, erro h.logger.Printf("Could not list jobs for circle=%s, topic=%s: %v", circle, t, err) continue } - + for _, jobID := range jobIDs { job, err := h.client.GetJob(jobID) if err != nil { h.logger.Printf("Error getting job %s: %v", jobID, err) continue } - + allJobs = append(allJobs, JobDisplayInfo{ - JobID: job.JobID, + JobID: fmt.Sprintf("%d", job.JobID), CircleID: job.CircleID, Topic: job.Topic, Status: string(job.Status), SessionKey: job.SessionKey, - HeroScript: job.HeroScript, - RhaiScript: job.RhaiScript, + Params: job.Params, + ParamsType: string(job.ParamsType), Result: job.Result, Error: job.Error, TimeScheduled: job.TimeScheduled, @@ -148,7 +143,7 @@ func (h *JobHandler) getJobsData(circleID, topic string) ([]JobDisplayInfo, erro } } } - + return allJobs, nil } else if circleID == "" || topic == "" { // If only one of the parameters is provided, we can't list jobs @@ -171,13 +166,13 @@ func (h *JobHandler) getJobsData(circleID, topic string) ([]JobDisplayInfo, erro } jobInfo := JobDisplayInfo{ - JobID: job.JobID, + JobID: fmt.Sprintf("%d", job.JobID), CircleID: job.CircleID, Topic: job.Topic, Status: string(job.Status), SessionKey: job.SessionKey, - HeroScript: job.HeroScript, - RhaiScript: job.RhaiScript, + Params: job.Params, + ParamsType: string(job.ParamsType), Result: job.Result, Error: job.Error, TimeScheduled: job.TimeScheduled, diff --git a/pkg/herojobs/processjob.go b/pkg/herojobs/processjob.go index 1b0b7b4..ff4d73d 100644 --- a/pkg/herojobs/processjob.go +++ b/pkg/herojobs/processjob.go @@ -72,7 +72,7 @@ func (d *WatchDog) processHeroScript(ctx context.Context, job *Job) { // TODO: Implement HeroScript processing errMsg := "HeroScript processing not implemented yet" - log.Printf("Error processing job %s: %s", job.JobID, errMsg) + log.Printf("Error processing job %d: %s", job.JobID, errMsg) // Update job status in OurDB job.Finish(JobStatusError, "", fmt.Errorf(errMsg))