init push

This commit is contained in:
2026-07-16 22:16:45 +07:00
commit 8b068bdb10
1021 changed files with 332816 additions and 0 deletions

View File

@@ -0,0 +1,374 @@
package service
import (
"context"
"testing"
"time"
filemanager "wucher/internal/domain/file_manager"
helicopterfile "wucher/internal/domain/helicopter_file"
helicopterfilerealtime "wucher/internal/realtime/helicopterfile"
"wucher/internal/shared/pkg/uuidv7"
)
type helicopterFileServiceRepoStub struct {
listPendingFn func(context.Context, int) ([]helicopterfile.HelicopterFile, error)
listPendingBySourceFn func(context.Context, []byte, int) ([]helicopterfile.HelicopterFile, error)
updateFn func(context.Context, *helicopterfile.HelicopterFile) error
}
func (r *helicopterFileServiceRepoStub) Create(context.Context, *helicopterfile.HelicopterFile) error {
return nil
}
func (r *helicopterFileServiceRepoStub) Update(ctx context.Context, row *helicopterfile.HelicopterFile) error {
if r.updateFn != nil {
return r.updateFn(ctx, row)
}
return nil
}
func (r *helicopterFileServiceRepoStub) Delete(context.Context, []byte) error { return nil }
func (r *helicopterFileServiceRepoStub) GetByID(context.Context, []byte) (*helicopterfile.HelicopterFile, error) {
return nil, nil
}
func (r *helicopterFileServiceRepoStub) List(context.Context, string, string, int, int) ([]helicopterfile.HelicopterFile, int64, error) {
return nil, 0, nil
}
func (r *helicopterFileServiceRepoStub) ListPendingAttachments(ctx context.Context, limit int) ([]helicopterfile.HelicopterFile, error) {
if r.listPendingFn != nil {
return r.listPendingFn(ctx, limit)
}
return nil, nil
}
func (r *helicopterFileServiceRepoStub) ListPendingAttachmentsBySourceFileID(ctx context.Context, sourceFileID []byte, limit int) ([]helicopterfile.HelicopterFile, error) {
if r.listPendingBySourceFn != nil {
return r.listPendingBySourceFn(ctx, sourceFileID, limit)
}
return nil, nil
}
func (r *helicopterFileServiceRepoStub) ListByHelicopter(context.Context, []byte) ([]helicopterfile.HelicopterFile, error) {
return nil, nil
}
func (r *helicopterFileServiceRepoStub) ListByHelicopterAndSection(context.Context, []byte, string) ([]helicopterfile.HelicopterFile, error) {
return nil, nil
}
func (r *helicopterFileServiceRepoStub) HelicopterExists(context.Context, []byte) (bool, error) {
return false, nil
}
func (r *helicopterFileServiceRepoStub) AttachmentExists(context.Context, []byte) (bool, error) {
return false, nil
}
type helicopterFileServiceManagerStub struct {
filemanager.Service
createAttachmentFn func(context.Context, filemanager.CreateAttachmentParams) (*filemanager.Attachment, error)
getAttachmentByIDFn func(context.Context, []byte) (*filemanager.Attachment, error)
markFileActiveFn func(context.Context, []byte, []byte) error
createAttachmentArgs []filemanager.CreateAttachmentParams
markFileActiveArgs []struct {
fileID []byte
actorID []byte
}
}
func (m *helicopterFileServiceManagerStub) CreateAttachment(ctx context.Context, params filemanager.CreateAttachmentParams) (*filemanager.Attachment, error) {
copied := params
copied.FileID = append([]byte(nil), params.FileID...)
copied.ActorID = append([]byte(nil), params.ActorID...)
m.createAttachmentArgs = append(m.createAttachmentArgs, copied)
if m.createAttachmentFn != nil {
return m.createAttachmentFn(ctx, params)
}
return nil, nil
}
func (m *helicopterFileServiceManagerStub) GetAttachmentByID(ctx context.Context, id []byte) (*filemanager.Attachment, error) {
if m.getAttachmentByIDFn != nil {
return m.getAttachmentByIDFn(ctx, id)
}
return nil, nil
}
func (m *helicopterFileServiceManagerStub) MarkFileActive(ctx context.Context, fileID []byte, actorID []byte) error {
m.markFileActiveArgs = append(m.markFileActiveArgs, struct {
fileID []byte
actorID []byte
}{fileID: append([]byte(nil), fileID...), actorID: append([]byte(nil), actorID...)})
if m.markFileActiveFn != nil {
return m.markFileActiveFn(ctx, fileID, actorID)
}
return nil
}
func (m *helicopterFileServiceManagerStub) ReconcileFileLifecycle(context.Context, []byte, []byte) error {
return nil
}
func (m *helicopterFileServiceManagerStub) CleanupOrphanPendingFiles(context.Context, time.Time, time.Duration, int, bool) (filemanager.FileCleanupStats, error) {
return filemanager.FileCleanupStats{}, nil
}
type helicopterFileServiceEventsStub struct {
publishedUsers []string
event *helicopterfilerealtime.Event
}
func (m *helicopterFileServiceEventsStub) PublishToUsers(userUUIDs []string, event helicopterfilerealtime.Event) int {
m.publishedUsers = append([]string(nil), userUUIDs...)
copied := event
m.event = &copied
return len(userUUIDs)
}
func TestHelicopterFileServiceFinalizePendingAttachmentsPublishesReadyEvent(t *testing.T) {
actorID := uuidv7.MustBytes()
helicopterID := uuidv7.MustBytes()
helicopterFileID := uuidv7.MustBytes()
sourceFileID := uuidv7.MustBytes()
attachmentID := uuidv7.MustBytes()
sourceFile := &filemanager.File{
ID: sourceFileID,
Name: "purchase_orders 1 (15).xlsx",
MimeType: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
Status: filemanager.FileStatusReady,
}
repo := &helicopterFileServiceRepoStub{
listPendingFn: func(_ context.Context, limit int) ([]helicopterfile.HelicopterFile, error) {
if limit != 1 {
t.Fatalf("expected limit 1, got %d", limit)
}
return []helicopterfile.HelicopterFile{
{
ID: helicopterFileID,
HelicopterID: helicopterID,
SourceFileID: sourceFileID,
CreatedBy: actorID,
UpdatedBy: actorID,
SourceFile: sourceFile,
Position: 4,
IsMandatory: false,
Section: helicopterfile.SectionBeforeFirstFlightInspection,
UpdatedAt: time.Unix(100, 0).UTC(),
CreatedAt: time.Unix(90, 0).UTC(),
FileAttachmentID: nil,
},
}, nil
},
updateFn: func(_ context.Context, row *helicopterfile.HelicopterFile) error {
if row == nil {
t.Fatal("expected row to be updated")
}
if len(row.FileAttachmentID) != 16 {
t.Fatalf("expected attachment id to be set, got %d bytes", len(row.FileAttachmentID))
}
if len(row.SourceFileID) != 0 {
t.Fatalf("expected source file id to be cleared, got %d bytes", len(row.SourceFileID))
}
return nil
},
}
fm := &helicopterFileServiceManagerStub{
createAttachmentFn: func(_ context.Context, params filemanager.CreateAttachmentParams) (*filemanager.Attachment, error) {
if got, want := uuidStringOrFail(t, params.FileID), uuidStringOrFail(t, sourceFileID); got != want {
t.Fatalf("unexpected file id: got %s want %s", got, want)
}
if got, want := params.RefType, "helicopter"; got != want {
t.Fatalf("unexpected ref type: got %q want %q", got, want)
}
if got, want := params.RefID, uuidStringOrFail(t, helicopterID); got != want {
t.Fatalf("unexpected ref id: got %q want %q", got, want)
}
if params.Category == nil || *params.Category != "helicopter_file" {
t.Fatalf("expected helicopter_file category, got %#v", params.Category)
}
return &filemanager.Attachment{
ID: attachmentID,
FileID: sourceFileID,
File: sourceFile,
}, nil
},
getAttachmentByIDFn: func(_ context.Context, id []byte) (*filemanager.Attachment, error) {
if got, want := uuidStringOrFail(t, id), uuidStringOrFail(t, attachmentID); got != want {
t.Fatalf("unexpected attachment lookup id: got %s want %s", got, want)
}
return &filemanager.Attachment{
ID: attachmentID,
FileID: sourceFileID,
File: sourceFile,
}, nil
},
}
events := &helicopterFileServiceEventsStub{}
svc := NewHelicopterFileService(repo).
WithFileManagerService(fm).
WithHelicopterFileEvents(events)
finalized, err := svc.FinalizePendingAttachments(context.Background(), 1)
if err != nil {
t.Fatalf("FinalizePendingAttachments returned error: %v", err)
}
if finalized != 1 {
t.Fatalf("expected one finalized attachment, got %d", finalized)
}
if len(fm.createAttachmentArgs) != 1 {
t.Fatalf("expected one attachment create call, got %d", len(fm.createAttachmentArgs))
}
if len(fm.markFileActiveArgs) != 1 {
t.Fatalf("expected one mark active call, got %d", len(fm.markFileActiveArgs))
}
if events.event == nil {
t.Fatal("expected helicopter file event to be published")
}
expectedUser := uuidStringOrFail(t, actorID)
if len(events.publishedUsers) != 1 || events.publishedUsers[0] != expectedUser {
t.Fatalf("unexpected published users: %#v", events.publishedUsers)
}
if events.event.EventType != helicopterfilerealtime.DefaultEventType {
t.Fatalf("expected default event type, got %q", events.event.EventType)
}
if events.event.HelicopterID != uuidStringOrFail(t, helicopterID) {
t.Fatalf("unexpected helicopter id: %+v", events.event)
}
if events.event.HelicopterFileID != uuidStringOrFail(t, helicopterFileID) {
t.Fatalf("unexpected helicopter file id: %+v", events.event)
}
if events.event.FileID != uuidStringOrFail(t, attachmentID) {
t.Fatalf("expected finalized event to point to attachment id, got %+v", events.event)
}
if events.event.AttachmentPending {
t.Fatalf("expected finalized event to mark attachment pending false, got %+v", events.event)
}
if events.event.Name != sourceFile.Name || events.event.MimeType != sourceFile.MimeType {
t.Fatalf("unexpected file metadata in event: %+v", events.event)
}
}
func TestHelicopterFileServiceFinalizePendingAttachmentsBySourceFileUUIDPublishesReadyEvent(t *testing.T) {
actorID := uuidv7.MustBytes()
helicopterID := uuidv7.MustBytes()
helicopterFileID := uuidv7.MustBytes()
sourceFileID := uuidv7.MustBytes()
attachmentID := uuidv7.MustBytes()
sourceFileUUID := uuidStringOrFail(t, sourceFileID)
sourceFile := &filemanager.File{
ID: sourceFileID,
Name: "purchase_orders 1 (15).xlsx",
MimeType: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
Status: filemanager.FileStatusReady,
}
repo := &helicopterFileServiceRepoStub{
listPendingBySourceFn: func(_ context.Context, source []byte, limit int) ([]helicopterfile.HelicopterFile, error) {
if got, want := uuidStringOrFail(t, source), sourceFileUUID; got != want {
t.Fatalf("unexpected source file id: got %s want %s", got, want)
}
if limit != 1 {
t.Fatalf("expected limit 1, got %d", limit)
}
return []helicopterfile.HelicopterFile{
{
ID: helicopterFileID,
HelicopterID: helicopterID,
SourceFileID: sourceFileID,
CreatedBy: actorID,
UpdatedBy: actorID,
SourceFile: sourceFile,
Position: 4,
IsMandatory: false,
Section: helicopterfile.SectionBeforeFirstFlightInspection,
UpdatedAt: time.Unix(100, 0).UTC(),
CreatedAt: time.Unix(90, 0).UTC(),
FileAttachmentID: nil,
},
}, nil
},
updateFn: func(_ context.Context, row *helicopterfile.HelicopterFile) error {
if row == nil {
t.Fatal("expected row to be updated")
}
if len(row.FileAttachmentID) != 16 {
t.Fatalf("expected attachment id to be set, got %d bytes", len(row.FileAttachmentID))
}
if len(row.SourceFileID) != 0 {
t.Fatalf("expected source file id to be cleared, got %d bytes", len(row.SourceFileID))
}
return nil
},
}
fm := &helicopterFileServiceManagerStub{
createAttachmentFn: func(_ context.Context, params filemanager.CreateAttachmentParams) (*filemanager.Attachment, error) {
if got, want := uuidStringOrFail(t, params.FileID), sourceFileUUID; got != want {
t.Fatalf("unexpected file id: got %s want %s", got, want)
}
return &filemanager.Attachment{
ID: attachmentID,
FileID: sourceFileID,
File: sourceFile,
}, nil
},
getAttachmentByIDFn: func(_ context.Context, id []byte) (*filemanager.Attachment, error) {
if got, want := uuidStringOrFail(t, id), uuidStringOrFail(t, attachmentID); got != want {
t.Fatalf("unexpected attachment lookup id: got %s want %s", got, want)
}
return &filemanager.Attachment{
ID: attachmentID,
FileID: sourceFileID,
File: sourceFile,
}, nil
},
}
events := &helicopterFileServiceEventsStub{}
svc := NewHelicopterFileService(repo).
WithFileManagerService(fm).
WithHelicopterFileEvents(events)
finalized, err := svc.FinalizePendingAttachmentsBySourceFileUUID(context.Background(), sourceFileUUID, 1)
if err != nil {
t.Fatalf("FinalizePendingAttachmentsBySourceFileUUID returned error: %v", err)
}
if finalized != 1 {
t.Fatalf("expected one finalized attachment, got %d", finalized)
}
if len(fm.createAttachmentArgs) != 1 {
t.Fatalf("expected one attachment create call, got %d", len(fm.createAttachmentArgs))
}
if len(fm.markFileActiveArgs) != 1 {
t.Fatalf("expected one mark active call, got %d", len(fm.markFileActiveArgs))
}
if events.event == nil {
t.Fatal("expected helicopter file event to be published")
}
if events.event.FileID != uuidStringOrFail(t, attachmentID) {
t.Fatalf("expected finalized event to point to attachment id, got %+v", events.event)
}
if events.event.AttachmentPending {
t.Fatalf("expected finalized event to mark attachment pending false, got %+v", events.event)
}
}
func uuidStringOrFail(t *testing.T, raw []byte) string {
t.Helper()
out, err := uuidv7.BytesToString(raw)
if err != nil {
t.Fatalf("convert uuid: %v", err)
}
return out
}