Initial commit of Descrybe v2 without local scratch artifacts.
Drop one-shot tmp/axe scripts and agent i18n scratch so the Gitea tree is deployable.
This commit is contained in:
@@ -0,0 +1,261 @@
|
||||
package processing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
func TestRetryJobResetsCompletedJobForFullRerun(t *testing.T) {
|
||||
dsn := os.Getenv("DATABASE_URL")
|
||||
if dsn == "" {
|
||||
t.Skip("DATABASE_URL not set")
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
defer cancel()
|
||||
|
||||
pg, err := pgxpool.New(ctx, dsn)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer pg.Close()
|
||||
|
||||
var companyID uuid.UUID
|
||||
err = pg.QueryRow(ctx, `
|
||||
SELECT company_id
|
||||
FROM raw_products
|
||||
WHERE company_id IS NOT NULL
|
||||
ORDER BY updated_at DESC
|
||||
LIMIT 1`).Scan(&companyID)
|
||||
if errorsIsNoRows(err) {
|
||||
t.Skip("no raw_products rows available")
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var userID uuid.UUID
|
||||
err = pg.QueryRow(ctx, `SELECT id FROM users ORDER BY created_at DESC LIMIT 1`).Scan(&userID)
|
||||
if errorsIsNoRows(err) {
|
||||
t.Skip("no users rows available")
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
rows, err := pg.Query(ctx, `
|
||||
SELECT id
|
||||
FROM raw_products
|
||||
WHERE company_id = $1
|
||||
ORDER BY updated_at DESC
|
||||
LIMIT 2`, companyID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
rawIDs := make([]uuid.UUID, 0, 2)
|
||||
for rows.Next() {
|
||||
var rawID uuid.UUID
|
||||
if err := rows.Scan(&rawID); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
rawIDs = append(rawIDs, rawID)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(rawIDs) == 0 {
|
||||
t.Skip("no raw_products for selected company")
|
||||
}
|
||||
|
||||
progress := InitialStepProgress("full")
|
||||
progressJSON, err := json.Marshal(progress)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
firstStep := ""
|
||||
if len(progress) > 0 {
|
||||
firstStep = progress[0].Step
|
||||
}
|
||||
|
||||
var jobID uuid.UUID
|
||||
err = pg.QueryRow(ctx, `
|
||||
INSERT INTO processing_jobs (
|
||||
company_id, user_id, status, total_products, processed_products,
|
||||
processing_type, current_step, step_progress, started_at, completed_at
|
||||
) VALUES ($1, $2, 'completed', $3, $3, 'full', $4, $5::jsonb, now(), now())
|
||||
RETURNING id`,
|
||||
companyID, userID, len(rawIDs), firstStep, progressJSON,
|
||||
).Scan(&jobID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer func() {
|
||||
_, _ = pg.Exec(context.Background(), `DELETE FROM processing_job_products WHERE job_id = $1`, jobID)
|
||||
_, _ = pg.Exec(context.Background(), `DELETE FROM processing_jobs WHERE id = $1`, jobID)
|
||||
}()
|
||||
|
||||
for _, rawID := range rawIDs {
|
||||
if _, err := pg.Exec(ctx, `
|
||||
INSERT INTO processing_job_products (job_id, raw_product_id, status)
|
||||
VALUES ($1, $2, 'processed')`, jobID, rawID); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
p := NewPipeline(pg)
|
||||
p.Billing = nil
|
||||
|
||||
job, err := p.RetryJob(ctx, companyID, jobID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if job.Status != "pending" {
|
||||
t.Fatalf("status=%q", job.Status)
|
||||
}
|
||||
if job.ProcessedProducts != 0 {
|
||||
t.Fatalf("processed_products=%d", job.ProcessedProducts)
|
||||
}
|
||||
if job.StartedAt != nil {
|
||||
t.Fatalf("started_at should be reset, got %v", *job.StartedAt)
|
||||
}
|
||||
if job.CompletedAt != nil {
|
||||
t.Fatalf("completed_at should be reset, got %v", *job.CompletedAt)
|
||||
}
|
||||
|
||||
var pendingCount, processedCount int
|
||||
err = pg.QueryRow(ctx, `
|
||||
SELECT
|
||||
COUNT(*) FILTER (WHERE status = 'pending'),
|
||||
COUNT(*) FILTER (WHERE status = 'processed')
|
||||
FROM processing_job_products
|
||||
WHERE job_id = $1`, jobID).Scan(&pendingCount, &processedCount)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if pendingCount != len(rawIDs) {
|
||||
t.Fatalf("pending_count=%d want %d", pendingCount, len(rawIDs))
|
||||
}
|
||||
if processedCount != 0 {
|
||||
t.Fatalf("processed_count=%d want 0", processedCount)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCancelJobCancelsPendingProducts(t *testing.T) {
|
||||
dsn := os.Getenv("DATABASE_URL")
|
||||
if dsn == "" {
|
||||
t.Skip("DATABASE_URL not set")
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
defer cancel()
|
||||
|
||||
pg, err := pgxpool.New(ctx, dsn)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer pg.Close()
|
||||
|
||||
var companyID uuid.UUID
|
||||
err = pg.QueryRow(ctx, `
|
||||
SELECT company_id
|
||||
FROM raw_products
|
||||
WHERE company_id IS NOT NULL
|
||||
ORDER BY updated_at DESC
|
||||
LIMIT 1`).Scan(&companyID)
|
||||
if errorsIsNoRows(err) {
|
||||
t.Skip("no raw_products rows available")
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var userID uuid.UUID
|
||||
err = pg.QueryRow(ctx, `SELECT id FROM users ORDER BY created_at DESC LIMIT 1`).Scan(&userID)
|
||||
if errorsIsNoRows(err) {
|
||||
t.Skip("no users rows available")
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var rawID uuid.UUID
|
||||
err = pg.QueryRow(ctx, `
|
||||
SELECT id
|
||||
FROM raw_products
|
||||
WHERE company_id = $1
|
||||
ORDER BY updated_at DESC
|
||||
LIMIT 1`, companyID).Scan(&rawID)
|
||||
if errorsIsNoRows(err) {
|
||||
t.Skip("no raw_products for selected company")
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
progress := InitialStepProgress("full")
|
||||
progressJSON, err := marshalStepProgress(progress)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
firstStep := ""
|
||||
if len(progress) > 0 {
|
||||
firstStep = progress[0].Step
|
||||
}
|
||||
|
||||
var jobID uuid.UUID
|
||||
err = pg.QueryRow(ctx, `
|
||||
INSERT INTO processing_jobs (
|
||||
company_id, user_id, status, total_products, processed_products,
|
||||
processing_type, current_step, step_progress, started_at
|
||||
) VALUES ($1, $2, 'running', 1, 0, 'full', $3, $4::jsonb, now())
|
||||
RETURNING id`,
|
||||
companyID, userID, firstStep, progressJSON,
|
||||
).Scan(&jobID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer func() {
|
||||
_, _ = pg.Exec(context.Background(), `DELETE FROM processing_job_products WHERE job_id = $1`, jobID)
|
||||
_, _ = pg.Exec(context.Background(), `DELETE FROM processing_jobs WHERE id = $1`, jobID)
|
||||
}()
|
||||
|
||||
if _, err := pg.Exec(ctx, `
|
||||
INSERT INTO processing_job_products (job_id, raw_product_id, status)
|
||||
VALUES ($1, $2, 'pending')`, jobID, rawID); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
p := NewPipeline(pg)
|
||||
p.Billing = nil
|
||||
|
||||
job, err := p.CancelJob(ctx, companyID, jobID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if job.Status != "cancelled" {
|
||||
t.Fatalf("status=%q", job.Status)
|
||||
}
|
||||
|
||||
var productStatus string
|
||||
err = pg.QueryRow(ctx, `
|
||||
SELECT status FROM processing_job_products WHERE job_id = $1`, jobID).Scan(&productStatus)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if productStatus != "cancelled" {
|
||||
t.Fatalf("product status=%q want cancelled", productStatus)
|
||||
}
|
||||
}
|
||||
|
||||
func errorsIsNoRows(err error) bool {
|
||||
return err == pgx.ErrNoRows
|
||||
}
|
||||
Reference in New Issue
Block a user