feat(t223): generate tasks from freight items
This commit is contained in:
@@ -395,6 +395,199 @@ func TestAdminFreightAPIImportsAllItemsWithoutPII(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAdminProcurementAPIProducesImmutablePendingTask(t *testing.T) {
|
||||
fixture := newAdminIntegrationFixture(t)
|
||||
createSync := performAdminRequest(
|
||||
t,
|
||||
fixture.router,
|
||||
http.MethodPost,
|
||||
"/api/v1/freight-syncs",
|
||||
"application/json",
|
||||
strings.NewReader(
|
||||
`{"mode":"ORDER_NUMBER","order_number":"SOURCE-12"}`,
|
||||
),
|
||||
"procurement-freight-sync",
|
||||
)
|
||||
var syncBody struct {
|
||||
Sync struct {
|
||||
ID string `json:"id"`
|
||||
} `json:"sync"`
|
||||
}
|
||||
decodeResponse(t, createSync, &syncBody)
|
||||
succeeded := false
|
||||
for attempt := 0; attempt < 50; attempt++ {
|
||||
status := performAdminRequest(
|
||||
t,
|
||||
fixture.router,
|
||||
http.MethodGet,
|
||||
"/api/v1/freight-syncs/"+syncBody.Sync.ID,
|
||||
"",
|
||||
nil,
|
||||
"",
|
||||
)
|
||||
if strings.Contains(status.Body.String(), `"status":"SUCCEEDED"`) {
|
||||
succeeded = true
|
||||
break
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
if !succeeded {
|
||||
t.Fatal("freight sync did not succeed")
|
||||
}
|
||||
orders := performAdminRequest(
|
||||
t,
|
||||
fixture.router,
|
||||
http.MethodGet,
|
||||
"/api/v1/freight-orders",
|
||||
"",
|
||||
nil,
|
||||
"",
|
||||
)
|
||||
var orderBody struct {
|
||||
Items []struct {
|
||||
ID string `json:"id"`
|
||||
} `json:"items"`
|
||||
}
|
||||
decodeResponse(t, orders, &orderBody)
|
||||
detail := performAdminRequest(
|
||||
t,
|
||||
fixture.router,
|
||||
http.MethodGet,
|
||||
"/api/v1/freight-orders/"+orderBody.Items[0].ID,
|
||||
"",
|
||||
nil,
|
||||
"",
|
||||
)
|
||||
var detailBody struct {
|
||||
Items []struct {
|
||||
ID string `json:"id"`
|
||||
} `json:"items"`
|
||||
}
|
||||
decodeResponse(t, detail, &detailBody)
|
||||
createRequest := performAdminRequest(
|
||||
t,
|
||||
fixture.router,
|
||||
http.MethodPost,
|
||||
"/api/v1/freight-items/"+detailBody.Items[0].ID+
|
||||
"/procurement-request",
|
||||
"application/json",
|
||||
strings.NewReader(`{"confirm_procurement_needed":true}`),
|
||||
"",
|
||||
)
|
||||
if createRequest.Code != http.StatusCreated ||
|
||||
!strings.Contains(createRequest.Body.String(), `"status":"NEEDS_IMAGE"`) {
|
||||
t.Fatalf(
|
||||
"create request status/body = %d / %s",
|
||||
createRequest.Code,
|
||||
createRequest.Body,
|
||||
)
|
||||
}
|
||||
var requestBody struct {
|
||||
Request struct {
|
||||
ID string `json:"id"`
|
||||
} `json:"procurement_request"`
|
||||
}
|
||||
decodeResponse(t, createRequest, &requestBody)
|
||||
imageBody, imageContentType := referenceUpload(t, "procurement-image")
|
||||
upload := performAdminRequest(
|
||||
t,
|
||||
fixture.router,
|
||||
http.MethodPost,
|
||||
"/api/v1/assets",
|
||||
imageContentType,
|
||||
imageBody,
|
||||
"procurement-image",
|
||||
)
|
||||
var assetBody struct {
|
||||
ID string `json:"id"`
|
||||
}
|
||||
decodeResponse(t, upload, &assetBody)
|
||||
bind := performAdminRequest(
|
||||
t,
|
||||
fixture.router,
|
||||
http.MethodPut,
|
||||
"/api/v1/procurement-requests/"+requestBody.Request.ID+
|
||||
"/reference-asset",
|
||||
"application/json",
|
||||
strings.NewReader(
|
||||
fmt.Sprintf(`{"image_asset_id":%q}`, assetBody.ID),
|
||||
),
|
||||
"",
|
||||
)
|
||||
if bind.Code != http.StatusOK ||
|
||||
!strings.Contains(bind.Body.String(), `"status":"READY"`) {
|
||||
t.Fatalf("bind status/body = %d / %s", bind.Code, bind.Body)
|
||||
}
|
||||
createTask := performAdminRequest(
|
||||
t,
|
||||
fixture.router,
|
||||
http.MethodPost,
|
||||
"/api/v1/procurement-requests/"+requestBody.Request.ID+
|
||||
"/purchase-task",
|
||||
"application/json",
|
||||
strings.NewReader(`{}`),
|
||||
"procurement-task-create",
|
||||
)
|
||||
if createTask.Code != http.StatusCreated ||
|
||||
!strings.Contains(createTask.Body.String(), `"status":"PENDING"`) {
|
||||
t.Fatalf(
|
||||
"create task status/body = %d / %s",
|
||||
createTask.Code,
|
||||
createTask.Body,
|
||||
)
|
||||
}
|
||||
var taskBody struct {
|
||||
Task struct {
|
||||
ID string `json:"id"`
|
||||
} `json:"task"`
|
||||
}
|
||||
decodeResponse(t, createTask, &taskBody)
|
||||
taskDetail := performAdminRequest(
|
||||
t,
|
||||
fixture.router,
|
||||
http.MethodGet,
|
||||
"/api/v1/tasks/"+taskBody.Task.ID,
|
||||
"",
|
||||
nil,
|
||||
"",
|
||||
)
|
||||
for _, required := range []string{
|
||||
`"title":"商品一"`,
|
||||
`"sku":"BLACK-L"`,
|
||||
`"quantity":1`,
|
||||
`"image_asset_id":"` + assetBody.ID + `"`,
|
||||
`"description":"ERP 规格:黑色,L"`,
|
||||
} {
|
||||
if !strings.Contains(taskDetail.Body.String(), required) {
|
||||
t.Fatalf("task detail missing %q: %s", required, taskDetail.Body)
|
||||
}
|
||||
}
|
||||
replay := performAdminRequest(
|
||||
t,
|
||||
fixture.router,
|
||||
http.MethodPost,
|
||||
"/api/v1/procurement-requests/"+requestBody.Request.ID+
|
||||
"/purchase-task",
|
||||
"application/json",
|
||||
strings.NewReader(`{}`),
|
||||
"procurement-task-replay",
|
||||
)
|
||||
if replay.Code != http.StatusCreated ||
|
||||
!strings.Contains(replay.Body.String(), `"replayed":true`) ||
|
||||
!strings.Contains(replay.Body.String(), taskBody.Task.ID) {
|
||||
t.Fatalf("task replay status/body = %d / %s", replay.Code, replay.Body)
|
||||
}
|
||||
var sourceCount int
|
||||
if err := fixture.db.QueryRow(
|
||||
`SELECT count(*) FROM purchase_task_sources
|
||||
WHERE task_id = ? AND procurement_request_id = ?`,
|
||||
taskBody.Task.ID,
|
||||
requestBody.Request.ID,
|
||||
).Scan(&sourceCount); err != nil || sourceCount != 1 {
|
||||
t.Fatalf("task source count = %d, error = %v", sourceCount, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAdminOrderAuthorizationIsIdempotentAndRevisioned(t *testing.T) {
|
||||
fixture := newAdminIntegrationFixture(t)
|
||||
taskID, executionID, taskHash, firstKey, secondKey :=
|
||||
@@ -515,6 +708,9 @@ func TestAdminOrderAuthorizationIsIdempotentAndRevisioned(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("migration.New() error = %v", err)
|
||||
}
|
||||
if err := runner.Down(context.Background()); err != nil {
|
||||
t.Fatalf("procurement migration down: %v", err)
|
||||
}
|
||||
if err := runner.Down(context.Background()); err != nil {
|
||||
t.Fatalf("freight migration down: %v", err)
|
||||
}
|
||||
@@ -852,6 +1048,14 @@ func newAdminIntegrationFixture(t *testing.T) *adminIntegrationFixture {
|
||||
if err != nil {
|
||||
t.Fatalf("usecase.NewFreightService() error = %v", err)
|
||||
}
|
||||
procurement, err := usecase.NewProcurementService(
|
||||
repositories,
|
||||
clock,
|
||||
ids,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("usecase.NewProcurementService() error = %v", err)
|
||||
}
|
||||
registrar, err := NewAdminRouteRegistrar(
|
||||
AdminServices{
|
||||
Assets: assets,
|
||||
@@ -859,6 +1063,7 @@ func newAdminIntegrationFixture(t *testing.T) *adminIntegrationFixture {
|
||||
Results: results,
|
||||
Authorizations: authorizations,
|
||||
Freight: freight,
|
||||
Procurement: procurement,
|
||||
},
|
||||
emptyAdminWeb{},
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user