FIX[BE]: if project flocs deleted kandangs reset to non_active and add filter get all project_flock by area,kandangs,period and location

This commit is contained in:
ragilap
2025-10-19 23:24:56 +07:00
parent c9b4b3008e
commit f15e0d62e3
13 changed files with 663 additions and 51 deletions
@@ -42,6 +42,7 @@ func setupIntegrationApp(t *testing.T) (*fiber.App, *gorm.DB) {
&entities.Location{},
&entities.Flock{},
&entities.ProjectFlock{},
&entities.ProjectFlockKandang{},
&entities.Kandang{},
&entities.Warehouse{},
&entities.Uom{},
@@ -191,6 +192,15 @@ func fetchCustomer(t *testing.T, db *gorm.DB, id uint) entities.Customer {
return customer
}
func fetchKandang(t *testing.T, db *gorm.DB, id uint) entities.Kandang {
t.Helper()
var kandang entities.Kandang
if err := db.Preload("ProjectFlock").First(&kandang, id).Error; err != nil {
t.Fatalf("failed to fetch kandang: %v", err)
}
return kandang
}
func createSupplier(t *testing.T, app *fiber.App, name, alias, category string) uint {
t.Helper()
identifier := strings.ToLower(strings.ReplaceAll(name, " ", "_"))
@@ -4,13 +4,17 @@ import (
"encoding/json"
"fmt"
"net/http"
"net/url"
"testing"
"github.com/gofiber/fiber/v2"
"gitlab.com/mbugroup/lti-api.git/internal/entities"
"gitlab.com/mbugroup/lti-api.git/internal/utils"
)
func TestProjectFlockSummary(t *testing.T) {
app, _ := setupIntegrationApp(t)
app, db := setupIntegrationApp(t)
areaID := createArea(t, app, "Area Project")
locationID := createLocation(t, app, "Location Project", "Address", areaID)
@@ -95,6 +99,21 @@ func TestProjectFlockSummary(t *testing.T) {
t.Fatalf("expected period 1 to be assigned automatically, got %d", createResp.Data.Period)
}
var pivotRecords []entities.ProjectFlockKandang
if err := db.Where("project_flock_id = ?", createResp.Data.Id).Find(&pivotRecords).Error; err != nil {
t.Fatalf("failed to fetch pivot records: %v", err)
}
if len(pivotRecords) != 1 {
t.Fatalf("expected 1 pivot record, got %d", len(pivotRecords))
}
firstPivotRecord := pivotRecords[0]
if firstPivotRecord.KandangId != kandangID {
t.Fatalf("expected pivot kandang id %d, got %d", kandangID, firstPivotRecord.KandangId)
}
if firstPivotRecord.DetachedAt != nil {
t.Fatalf("expected pivot DetachedAt to be nil for active assignment, got %v", firstPivotRecord.DetachedAt)
}
secondKandangID := createKandang(t, app, "Kandang Summary 2", locationID, 1)
secondPayload := map[string]any{
"flock_id": flockID,
@@ -121,6 +140,21 @@ func TestProjectFlockSummary(t *testing.T) {
t.Fatalf("expected second period to be 2, got %d", createRespSecond.Data.Period)
}
pivotRecords = nil
if err := db.Where("project_flock_id = ?", createRespSecond.Data.Id).Find(&pivotRecords).Error; err != nil {
t.Fatalf("failed to fetch second pivot records: %v", err)
}
if len(pivotRecords) != 1 {
t.Fatalf("expected 1 pivot record for second project, got %d", len(pivotRecords))
}
secondPivotRecord := pivotRecords[0]
if secondPivotRecord.KandangId != secondKandangID {
t.Fatalf("expected second pivot kandang id %d, got %d", secondKandangID, secondPivotRecord.KandangId)
}
if secondPivotRecord.DetachedAt != nil {
t.Fatalf("expected second pivot DetachedAt to be nil, got %v", secondPivotRecord.DetachedAt)
}
resp, body = doJSONRequest(t, app, http.MethodGet, "/api/production/project_flocks/flocks/"+uintToString(flockID)+"/periods", nil)
if resp.StatusCode != fiber.StatusOK {
t.Fatalf("expected 200 when fetching summary, got %d: %s", resp.StatusCode, string(body))
@@ -144,11 +178,49 @@ func TestProjectFlockSummary(t *testing.T) {
t.Fatalf("expected 200 when deleting first project flock, got %d: %s", resp.StatusCode, string(body))
}
firstKandang := fetchKandang(t, db, kandangID)
if firstKandang.ProjectFlockId != nil {
t.Fatalf("expected project_flock_id to be nil after delete, got %v", *firstKandang.ProjectFlockId)
}
if firstKandang.Status != string(utils.KandangStatusNonActive) {
t.Fatalf("expected kandang status to revert to NON_ACTIVE, got %s", firstKandang.Status)
}
var firstPivot entities.ProjectFlockKandang
if err := db.First(&firstPivot, firstPivotRecord.Id).Error; err != nil {
t.Fatalf("failed to reload first pivot record: %v", err)
}
if firstPivot.DetachedAt == nil {
t.Fatalf("expected first pivot DetachedAt to be set after delete")
}
if firstPivot.ProjectFlockId != createResp.Data.Id {
t.Fatalf("expected first pivot project_flock_id %d, got %d", createResp.Data.Id, firstPivot.ProjectFlockId)
}
resp, body = doJSONRequest(t, app, http.MethodDelete, "/api/production/project_flocks/"+uintToString(createRespSecond.Data.Id), nil)
if resp.StatusCode != fiber.StatusOK {
t.Fatalf("expected 200 when deleting second project flock, got %d: %s", resp.StatusCode, string(body))
}
secondKandang := fetchKandang(t, db, secondKandangID)
if secondKandang.ProjectFlockId != nil {
t.Fatalf("expected second project_flock_id to be nil after delete, got %v", *secondKandang.ProjectFlockId)
}
if secondKandang.Status != string(utils.KandangStatusNonActive) {
t.Fatalf("expected second kandang status to revert to NON_ACTIVE, got %s", secondKandang.Status)
}
var secondPivot entities.ProjectFlockKandang
if err := db.First(&secondPivot, secondPivotRecord.Id).Error; err != nil {
t.Fatalf("failed to reload second pivot record: %v", err)
}
if secondPivot.DetachedAt == nil {
t.Fatalf("expected second pivot DetachedAt to be set after delete")
}
if secondPivot.ProjectFlockId != createRespSecond.Data.Id {
t.Fatalf("expected second pivot project_flock_id %d, got %d", createRespSecond.Data.Id, secondPivot.ProjectFlockId)
}
resp, body = doJSONRequest(t, app, http.MethodGet, "/api/production/project_flocks/flocks/"+uintToString(flockID)+"/periods", nil)
if resp.StatusCode != fiber.StatusOK {
t.Fatalf("expected 200 when fetching summary after delete, got %d: %s", resp.StatusCode, string(body))
@@ -166,3 +238,178 @@ func TestProjectFlockSummary(t *testing.T) {
func uintToString(v uint) string {
return fmt.Sprintf("%d", v)
}
func TestProjectFlockSearchByRelatedFields(t *testing.T) {
app, _ := setupIntegrationApp(t)
areaID := createArea(t, app, "Area Search Target")
locationID := createLocation(t, app, "Location Search Target", "Location Address Target", areaID)
flockID := createFlock(t, app, "Flock Search Target")
categoryID := createProductCategory(t, app, "Category Search Target", "CATGT")
fcrID := createFcr(t, app, "FCR Search Target", []map[string]any{
{"weight": 1.0, "fcr_number": 1.5, "mortality": 2.0},
})
kandangID := createKandang(t, app, "Kandang Search Target", locationID, 1)
createPayload := map[string]any{
"flock_id": flockID,
"area_id": areaID,
"product_category_id": categoryID,
"fcr_id": fcrID,
"location_id": locationID,
"kandang_ids": []uint{kandangID},
}
resp, body := doJSONRequest(t, app, http.MethodPost, "/api/production/project_flocks", createPayload)
if resp.StatusCode != fiber.StatusCreated {
t.Fatalf("expected 201 when creating project flock, got %d: %s", resp.StatusCode, string(body))
}
var createResp struct {
Data struct {
Id uint `json:"id"`
} `json:"data"`
}
if err := json.Unmarshal(body, &createResp); err != nil {
t.Fatalf("failed to parse create response: %v", err)
}
searchTerms := []string{
"Flock Search Target",
"Area Search Target",
"Category Search Target",
"CATGT",
"FCR Search Target",
"Kandang Search Target",
"Location Search Target",
"Location Address Target",
"Tester",
"1",
}
for _, term := range searchTerms {
path := "/api/production/project_flocks?search=" + url.QueryEscape(term)
resp, body := doJSONRequest(t, app, http.MethodGet, path, nil)
if resp.StatusCode != fiber.StatusOK {
t.Fatalf("expected 200 when searching for %q, got %d: %s", term, resp.StatusCode, string(body))
}
var listResp struct {
Data []struct {
Id uint `json:"id"`
} `json:"data"`
Meta struct {
TotalResults int64 `json:"total_results"`
} `json:"meta"`
}
if err := json.Unmarshal(body, &listResp); err != nil {
t.Fatalf("failed to parse list response for %q: %v", term, err)
}
if listResp.Meta.TotalResults == 0 {
t.Fatalf("expected at least one result when searching for %q", term)
}
if len(listResp.Data) == 0 {
t.Fatalf("expected data when searching for %q", term)
}
if listResp.Data[0].Id != createResp.Data.Id {
t.Fatalf("expected project flock id %d for search term %q, got %d", createResp.Data.Id, term, listResp.Data[0].Id)
}
}
}
func TestProjectFlockSorting(t *testing.T) {
app, _ := setupIntegrationApp(t)
areaA := createArea(t, app, "Area Alpha")
areaB := createArea(t, app, "Area Beta")
locationA := createLocation(t, app, "Location Alpha", "Address Alpha", areaA)
locationB := createLocation(t, app, "Location Beta", "Address Beta", areaB)
flockOne := createFlock(t, app, "Flock Sort One")
flockTwo := createFlock(t, app, "Flock Sort Two")
categoryID := createProductCategory(t, app, "Category Sort", "CSORT")
fcrID := createFcr(t, app, "FCR Sort", []map[string]any{
{"weight": 1.0, "fcr_number": 1.5, "mortality": 2.0},
})
kandangOne := createKandang(t, app, "Kandang Sort One", locationA, 1)
kandangTwo := createKandang(t, app, "Kandang Sort Two", locationB, 1)
kandangThree := createKandang(t, app, "Kandang Sort Three", locationB, 1)
projectOnePayload := map[string]any{
"flock_id": flockOne,
"area_id": areaA,
"product_category_id": categoryID,
"fcr_id": fcrID,
"location_id": locationA,
"kandang_ids": []uint{kandangOne},
}
resp, body := doJSONRequest(t, app, http.MethodPost, "/api/production/project_flocks", projectOnePayload)
if resp.StatusCode != fiber.StatusCreated {
t.Fatalf("expected 201 for project one, got %d: %s", resp.StatusCode, string(body))
}
projectOneID := parseProjectFlockID(t, body)
projectTwoPayload := map[string]any{
"flock_id": flockTwo,
"area_id": areaB,
"product_category_id": categoryID,
"fcr_id": fcrID,
"location_id": locationB,
"kandang_ids": []uint{kandangTwo, kandangThree},
}
resp, body = doJSONRequest(t, app, http.MethodPost, "/api/production/project_flocks", projectTwoPayload)
if resp.StatusCode != fiber.StatusCreated {
t.Fatalf("expected 201 for project two, got %d: %s", resp.StatusCode, string(body))
}
projectTwoID := parseProjectFlockID(t, body)
updatePeriodPayload := map[string]any{"period": 5}
resp, body = doJSONRequest(t, app, http.MethodPatch, "/api/production/project_flocks/"+uintToString(projectTwoID), updatePeriodPayload)
if resp.StatusCode != fiber.StatusOK {
t.Fatalf("expected 200 when updating period, got %d: %s", resp.StatusCode, string(body))
}
assertOrder := func(t *testing.T, app *fiber.App, query string, expectedFirst uint) {
t.Helper()
resp, body := doJSONRequest(t, app, http.MethodGet, "/api/production/project_flocks?"+query, nil)
if resp.StatusCode != fiber.StatusOK {
t.Fatalf("expected 200 for query %q, got %d: %s", query, resp.StatusCode, string(body))
}
var listResp struct {
Data []struct {
Id uint `json:"id"`
} `json:"data"`
}
if err := json.Unmarshal(body, &listResp); err != nil {
t.Fatalf("failed to parse list response for %q: %v", query, err)
}
if len(listResp.Data) == 0 {
t.Fatalf("expected data for query %q", query)
}
if listResp.Data[0].Id != expectedFirst {
t.Fatalf("expected first id %d for query %q, got %d", expectedFirst, query, listResp.Data[0].Id)
}
}
assertOrder(t, app, "sort_by=area&sort_order=asc", projectOneID)
assertOrder(t, app, "sort_by=location&sort_order=desc", projectTwoID)
assertOrder(t, app, "sort_by=period&sort_order=desc", projectTwoID)
assertOrder(t, app, "sort_by=kandangs&sort_order=desc", projectTwoID)
assertOrder(t, app, "sort_by=kandangs&sort_order=asc", projectOneID)
}
func parseProjectFlockID(t *testing.T, body []byte) uint {
t.Helper()
var resp struct {
Data struct {
Id uint `json:"id"`
} `json:"data"`
}
if err := json.Unmarshal(body, &resp); err != nil {
t.Fatalf("failed to parse project flock response: %v", err)
}
return resp.Data.Id
}