feat(testing): add full integration test suite
Some checks failed
Integration Tests / integration-test (push) Failing after -23m59s

This commit introduces a comprehensive integration test suite for the pgsql-broker.

The test suite includes:
- A Docker/Podman environment for running a PostgreSQL database, managed via a .
- Integration tests that cover the broker's lifecycle, including job creation, execution, and instance management.
- A GitHub Actions workflow to automate the execution of all tests on push and pull requests.
- A dedicated test configuration file () and helper test files.

refactor(worker): fix job processing transaction
- The worker's job processing now uses a single transaction to fetch and run a job, resolving a race condition where jobs were not in the 'running' state when being executed.
- The broker's database instance registration is now more robust, handling cases where another instance is already active.

The Makefile has been significantly updated to orchestrate the entire test flow, including setting up the database, starting/stopping the broker, and running unit and integration tests separately.
This commit is contained in:
2026-01-02 23:08:17 +02:00
parent 19e469ff54
commit 3e64f7ae2a
16 changed files with 406 additions and 34 deletions

View File

@@ -2,6 +2,7 @@ package broker
import (
"context"
"database/sql" // Import sql package
"encoding/json"
"fmt"
"os"
@@ -133,22 +134,68 @@ func (i *DatabaseInstance) Stop() error {
func (i *DatabaseInstance) registerInstance() error {
var retval int
var errmsg string
var instanceID int64
var nullableInstanceID sql.NullInt64 // Change to nullable type
i.logger.Debug("registering instance", "name", i.Name, "hostname", i.Hostname, "pid", i.PID, "version", i.Version, "queue_count", i.dbConfig.QueueCount)
err := i.db.QueryRow(i.ctx,
"SELECT p_retval, p_errmsg, p_instance_id FROM broker_register_instance($1, $2, $3, $4, $5)",
i.Name, i.Hostname, i.PID, i.Version, i.dbConfig.QueueCount,
).Scan(&retval, &errmsg, &instanceID)
).Scan(&retval, &errmsg, &nullableInstanceID)
if err != nil {
i.logger.Error("query error during instance registration", "error", err)
return fmt.Errorf("query error: %w", err)
}
if retval > 0 {
if retval == 3 {
i.logger.Warn("another broker instance is already active, attempting to retrieve ID", "error", errmsg)
// Try to retrieve the ID of the active instance
var activeID int64
err := i.db.QueryRow(i.ctx,
"SELECT id_broker_queueinstance FROM broker_queueinstance WHERE name = $1 AND hostname = $2 AND status = 'active' ORDER BY started_at DESC LIMIT 1",
i.Name, i.Hostname,
).Scan(&activeID)
if err != nil {
i.logger.Error("failed to retrieve ID of active instance", "error", err)
return fmt.Errorf("failed to retrieve ID of active instance: %w", err)
}
i.ID = activeID
i.logger.Info("retrieved active instance ID", "id", i.ID)
return nil
} else if retval > 0 {
i.logger.Error("broker_register_instance error", "retval", retval, "errmsg", errmsg)
return fmt.Errorf("broker_register_instance error: %s", errmsg)
}
i.ID = instanceID
// If successfully registered, nullableInstanceID.Valid will be true
if nullableInstanceID.Valid {
i.ID = nullableInstanceID.Int64
i.logger.Info("registered new instance", "id", i.ID)
// Debug logging: Retrieve all entries from broker_queueinstance
rows, err := i.db.Query(i.ctx, "SELECT id_broker_queueinstance, name, hostname, status FROM broker_queueinstance")
if err != nil {
i.logger.Error("debug query failed", "error", err)
} else {
defer rows.Close()
for rows.Next() {
var id int64
var name, hostname, status string
if err := rows.Scan(&id, &name, &hostname, &status); err != nil {
i.logger.Error("debug scan failed", "error", err)
break
}
i.logger.Debug("broker_queueinstance entry", "id", id, "name", name, "hostname", hostname, "status", status)
}
}
} else {
// This case should ideally not happen if retval is 0 (success)
// but if it does, it means p_instance_id was NULL despite success.
// This would be an unexpected scenario.
i.logger.Error("broker_register_instance returned success but no instance ID", "retval", retval, "errmsg", errmsg)
return fmt.Errorf("broker_register_instance returned success but no instance ID")
}
return nil
}
@@ -323,4 +370,4 @@ func (i *DatabaseInstance) GetStats() map[string]interface{} {
stats["queues"] = queueStats
return stats
}
}