add ocr pipeline

This commit is contained in:
m
2026-09-15 23:52:19 +02:00
parent 102cc425b9
commit 050434325a
21 changed files with 1028 additions and 17 deletions
+8 -1
View File
@@ -85,6 +85,13 @@ func FilesUpload(c *gin.Context) {
}
userID := c.GetString(UserIDKey)
folderID := c.PostForm("folderId")
// resourceId optionnel : cible un fichier déjà connu (metadata-only de
// l'outbox) pour lui attacher ses octets physiques (ex. OCR serveur).
resourceID := c.PostForm("resourceId")
if resourceID != "" && !deviceIDPattern.MatchString(resourceID) {
api.Error(c, http.StatusNotFound, "NOT_FOUND", "file not found")
return
}
c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, Store.MaxFileSize+1)
file, err := c.FormFile("file")
@@ -102,7 +109,7 @@ func FilesUpload(c *gin.Context) {
return
}
dto, err := Store.Upload(userID, file, folderID)
dto, err := Store.Upload(userID, file, folderID, resourceID)
if err != nil {
writeError(c, err)
return
+84
View File
@@ -183,6 +183,29 @@ func uploadMultipart(t *testing.T, r *gin.Engine, token, folderID, filename stri
return rec
}
// uploadMultipartToResource uploads bytes targeted at an existing resource_id
// (metadata-only file from the outbox), le cas d'usage OCR.
func uploadMultipartToResource(t *testing.T, r *gin.Engine, token, resourceID, filename string, content []byte) *httptest.ResponseRecorder {
t.Helper()
var body bytes.Buffer
writer := multipart.NewWriter(&body)
part, err := writer.CreateFormFile("file", filename)
if err != nil {
t.Fatalf("create form file: %v", err)
}
if _, err := part.Write(content); err != nil {
t.Fatalf("write body: %v", err)
}
if err := writer.WriteField("resourceId", resourceID); err != nil {
t.Fatalf("write resourceId: %v", err)
}
if err := writer.Close(); err != nil {
t.Fatalf("close writer: %v", err)
}
rec, _ := doRequest(t, r, http.MethodPost, "/api/v1/files/upload", token, body.Bytes(), writer.FormDataContentType())
return rec
}
func TestFilesFlow(t *testing.T) {
r, _, repo := setup(t)
deviceA := repository.NewID()
@@ -385,6 +408,67 @@ func TestUploadNameConflict(t *testing.T) {
expectError(t, rec, http.StatusConflict, "NAME_CONFLICT", "upload-dupe")
}
func TestUploadTargetsExistingResource(t *testing.T) {
r, _, repo := setup(t)
device := repository.NewID()
token, user := registerAndLogin(t, r, repo, testUserUsername(device, "tr"), "upload-test-password", device)
// Fiche metadata-only (même sémantique que l'outbox create_resource).
resourceID := repository.NewID()
if err := repo.Resources.InsertFile(user, resourceID, "scan.png", "", 0, nil, nil); err != nil {
t.Fatalf("insert metadata-only file: %v", err)
}
rec := uploadMultipartToResource(t, r, token, resourceID, "scan.png", []byte("PNGDATA"))
env := expectOK(t, rec, "upload-target")
var uploaded fileDTO
if err := json.Unmarshal(env.Data, &uploaded); err != nil {
t.Fatalf("upload-target: unmarshal: %v", err)
}
if uploaded.ID != resourceID {
t.Errorf("id attendu %s, got %s", resourceID, uploaded.ID)
}
if uploaded.Size != 7 {
t.Errorf("taille = %d, attendu 7", uploaded.Size)
}
// Toujours une seule ligne (pas de doublon).
rec, _ = doRequest(t, r, http.MethodGet, "/api/v1/files", token, nil, "")
env = expectOK(t, rec, "list-after-targeted-upload")
var files []fileDTO
if err := json.Unmarshal(env.Data, &files); err != nil {
t.Fatalf("list: unmarshal: %v", err)
}
if len(files) != 1 || files[0].ID != resourceID {
t.Errorf("aucun doublon attendu, got %+v", files)
}
}
func TestUploadTargetForeignResourceNotFound(t *testing.T) {
r, _, repo := setup(t)
deviceA := repository.NewID()
deviceB := repository.NewID()
tokenA, _ := registerAndLogin(t, r, repo, testUserUsername(deviceA, "tf"), "upload-test-password", deviceA)
_, userB := registerAndLogin(t, r, repo, testUserUsername(deviceB, "tg"), "upload-test-password-b", deviceB)
resourceID := repository.NewID()
if err := repo.Resources.InsertFile(userB, resourceID, "mine.png", "", 0, nil, nil); err != nil {
t.Fatalf("insert B file: %v", err)
}
rec := uploadMultipartToResource(t, r, tokenA, resourceID, "mine.png", []byte("DATA"))
expectError(t, rec, http.StatusNotFound, "NOT_FOUND", "upload-foreign")
}
func TestUploadTargetInvalidID(t *testing.T) {
r, _, repo := setup(t)
device := repository.NewID()
token, _ := registerAndLogin(t, r, repo, testUserUsername(device, "ti"), "upload-test-password", device)
rec := uploadMultipartToResource(t, r, token, "NOT-HEX", "x.png", []byte("DATA"))
expectError(t, rec, http.StatusNotFound, "NOT_FOUND", "upload-invalid-id")
}
func TestFoldersListAndScoping(t *testing.T) {
r, _, repo := setup(t)
deviceA := repository.NewID()