Complete artifact binary transfer streaming

This commit is contained in:
npc0-hue
2026-09-03 13:27:22 +08:00
parent fe09d21a56
commit ec2462a312
8 changed files with 178 additions and 35 deletions
@@ -1,8 +1,10 @@
package api
import (
"bytes"
"net/http"
"net/http/httptest"
"strconv"
"testing"
"browser.local/platform/domain"
@@ -23,14 +25,14 @@ func TestArtifactTransferAPIWorkflow(t *testing.T) {
t.Fatalf("unexpected open response: %+v", opened)
}
chunkRecorder := performJSON(t, router, http.MethodPost, "/api/v1/run/artifacts/chunks", validArtifactChunkRequest(hello.SessionToken, opened.TransferID, payload, 0, 8))
chunkRecorder := performArtifactChunkUpload(t, router, validArtifactChunkRequest(hello.SessionToken, opened.TransferID, payload, 0, 8))
assertStatus(t, chunkRecorder, http.StatusOK)
chunk := decodeBody[dto.ArtifactChunkUploadResponse](t, chunkRecorder)
if !chunk.Accepted || chunk.NextMissingChunkIndex != 1 || len(chunk.ReceivedChunkIndexes) != 1 {
t.Fatalf("unexpected chunk response: %+v", chunk)
}
duplicateRecorder := performJSON(t, router, http.MethodPost, "/api/v1/run/artifacts/chunks", validArtifactChunkRequest(hello.SessionToken, opened.TransferID, payload, 0, 8))
duplicateRecorder := performArtifactChunkUpload(t, router, validArtifactChunkRequest(hello.SessionToken, opened.TransferID, payload, 0, 8))
assertStatus(t, duplicateRecorder, http.StatusOK)
duplicate := decodeBody[dto.ArtifactChunkUploadResponse](t, duplicateRecorder)
if !duplicate.Duplicate {
@@ -48,7 +50,7 @@ func TestArtifactTransferAPIWorkflow(t *testing.T) {
assertErrorResponse(t, missingComplete, http.StatusBadRequest, errorCodeValidation)
for index := 1; index < opened.TotalChunks; index++ {
partRecorder := performJSON(t, router, http.MethodPost, "/api/v1/run/artifacts/chunks", validArtifactChunkRequest(hello.SessionToken, opened.TransferID, payload, index, 8))
partRecorder := performArtifactChunkUpload(t, router, validArtifactChunkRequest(hello.SessionToken, opened.TransferID, payload, index, 8))
assertStatus(t, partRecorder, http.StatusOK)
}
completeRecorder := performJSON(t, router, http.MethodPost, "/api/v1/run/artifacts/complete", dto.ArtifactTransferCompleteRequest{RunEndpointID: "run-local", SessionToken: hello.SessionToken, TransferID: opened.TransferID, ArtifactID: "artifact-1", Checksum: validator.BytesChecksum(payload), SizeBytes: int64(len(payload))})
@@ -68,7 +70,7 @@ func TestArtifactTransferAPIErrors(t *testing.T) {
badChunk := validArtifactChunkRequest(hello.SessionToken, opened.TransferID, payload, 0, 8)
badChunk.Checksum = validator.BytesChecksum([]byte("different"))
badChunkRecorder := performJSON(t, router, http.MethodPost, "/api/v1/run/artifacts/chunks", badChunk)
badChunkRecorder := performArtifactChunkUpload(t, router, badChunk)
assertErrorResponse(t, badChunkRecorder, http.StatusBadRequest, errorCodeValidation)
invalidSession := validArtifactTransferOpenRequest("stale-token", payload, 8)
@@ -108,6 +110,23 @@ func performArtifactTransferOpen(t *testing.T, router http.Handler, request dto.
return recorder
}
func performArtifactChunkUpload(t *testing.T, router http.Handler, request dto.ArtifactChunkUploadRequest) *httptest.ResponseRecorder {
t.Helper()
req := httptest.NewRequest(http.MethodPost, "/api/v1/run/artifacts/chunks", bytes.NewReader(request.Payload))
req.Header.Set("Content-Type", "application/octet-stream")
req.Header.Set("X-Run-Endpoint", request.RunEndpointID)
req.Header.Set("X-Run-Session-Token", request.SessionToken)
req.Header.Set("X-Artifact-Transfer-Id", request.TransferID)
req.Header.Set("X-Artifact-Id", request.ArtifactID)
req.Header.Set("X-Artifact-Chunk-Index", strconv.Itoa(request.ChunkIndex))
req.Header.Set("X-Artifact-Offset", strconv.FormatInt(request.Offset, 10))
req.Header.Set("X-Artifact-Size", strconv.Itoa(request.SizeBytes))
req.Header.Set("X-Artifact-Checksum", request.Checksum)
recorder := httptest.NewRecorder()
router.ServeHTTP(recorder, req)
return recorder
}
func validArtifactTransferOpenRequest(sessionToken string, payload []byte, chunkSize int) dto.ArtifactTransferOpenRequest {
return dto.ArtifactTransferOpenRequest{
RunEndpointID: "run-local",