add gitlab ci

This commit is contained in:
m
2026-09-16 14:47:10 +02:00
parent 906d48702c
commit cd4f0179fc
32 changed files with 1462 additions and 40 deletions
+8
View File
@@ -127,6 +127,14 @@ func (o *Ocr) process(ctx context.Context, row repository.OcrJobRow) {
return
}
_ = o.Repository.OcrJobs.Complete(row.DeviceID, row.ID, text)
// Le texte atterrit sur la ressource (updated_at bumpé) : consultable par
// tous les devices du user et les grantees via GET /files/:id/ocr. SILENCIEUX
// (hôte déjà supprimé ou lock d'écriture) : le job reste source d'historique.
if trimmed := strings.TrimSpace(text); trimmed != "" {
if err := o.Repository.Resources.UpdateOcrText(userID, row.FileID, trimmed); err != nil {
log.Printf("ocr: persiste ocr_text de %s: %v", row.FileID, err)
}
}
}
// physicalPath résout UPLOAD_DIR/<user_id>/<resource_id>.<ext> — l'ext est
+53
View File
@@ -110,6 +110,59 @@ func TestOcrJobFailsWhenPhysicalFileMissing(t *testing.T) {
}
}
func TestOcrJobPersistsTextOnResourceAndGrantsRead(t *testing.T) {
uploadDir := t.TempDir()
o, ownerID, deviceID, _ := newTestOcr(t, uploadDir, stubEngine{text: " extrait du scan "})
granteeID := mustCreateUser(t, o.Repository, "ocr-grantee")
outsiderID := mustCreateUser(t, o.Repository, "ocr-outsider")
mustRegisterDevice(t, o.Repository, shareTestDeviceID)
fileID := repository.NewID()
insertOcrFile(t, o, ownerID, fileID)
if err := os.MkdirAll(filepath.Join(uploadDir, ownerID), 0o755); err != nil {
t.Fatalf("mkdir: %v", err)
}
if err := os.WriteFile(filepath.Join(uploadDir, ownerID, fileID+".png"), []byte("img"), 0o644); err != nil {
t.Fatalf("write: %v", err)
}
job, err := o.Create(ownerID, deviceID, fileID)
if err != nil {
t.Fatalf("create: %v", err)
}
row := waitTillTerminal(t, o, deviceID, job.ID)
if row.Status != "done" {
t.Fatalf("job %s: attendu done", row.Status)
}
// Le texte (trimé) a atterri sur la ressource, lisible par l'owner.
s := NewResources(o.Repository, uploadDir, 100)
owned, err := s.GetFileOcr(ownerID, fileID)
if err != nil {
t.Fatalf("get ocr owner: %v", err)
}
if owned.Text != "extrait du scan" {
t.Errorf("ocr_text = %q, attendu %q", owned.Text, "extrait du scan")
}
// ... et par un grantee viewer+ (docs §5 : partage rend le texte lisible).
if err := o.Repository.Shares.Upsert(fileID, granteeID, "viewer", false, nil, ownerID); err != nil {
t.Fatalf("share: %v", err)
}
shared, err := s.GetFileOcr(granteeID, fileID)
if err != nil {
t.Fatalf("get ocr grantee: %v", err)
}
if shared.Text != "extrait du scan" {
t.Errorf("ocr_text grantee = %q, attendu %q", shared.Text, "extrait du scan")
}
// Un tiers n'y a pas accès.
if _, err := s.GetFileOcr(outsiderID, fileID); !errors.Is(err, repository.ErrNotFound) {
t.Errorf("tiers : attendu ErrNotFound, got %v", err)
}
}
func TestOcrJobFailsOnEngineError(t *testing.T) {
uploadDir := t.TempDir()
o, userID, deviceID, _ := newTestOcr(t, uploadDir, stubEngine{err: errors.New("tesseract boom")})
+20
View File
@@ -73,6 +73,26 @@ func (s *Resources) GetFile(ownerID, id string) (FileDTO, error) {
return toFileDTO(row), nil
}
// FileOcrDTO is the payload of GET /files/:id/ocr: the server-computed OCR
// extract of the resource, readable by its owner and by grantees (viewer+).
type FileOcrDTO struct {
Text string `json:"text"`
UpdatedAt string `json:"updatedAt,omitempty"`
}
// GetFileOcr returns the OCR text of a file the user can see (viewer+, own or
// shared). Absent text → Text empty ("") with 200 (the caller decides).
func (s *Resources) GetFileOcr(ownerID, id string) (FileOcrDTO, error) {
row, err := s.Repo.GetFileVisible(ownerID, id)
if err != nil {
return FileOcrDTO{}, err
}
return FileOcrDTO{
Text: row.OcrText,
UpdatedAt: row.UpdatedAt.UTC().Format(time.RFC3339),
}, nil
}
func (s *Resources) DeleteFile(ownerID, id string) (string, error) {
return s.Repo.DeleteFile(ownerID, id)
}