package freedoc_test import ( "context" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "chat-agent-system/internal/docrevision" "chat-agent-system/internal/freedoc" ) func TestCreateDocument_ClientKeyIdempotentAndBootstrapsLedger(t *testing.T) { ctx := context.Background() rev := docrevision.NewService(nil) svc := freedoc.NewService(nil, nil) svc.SetDocRevision(rev) detail, err := svc.CreateDocument(ctx, 42, freedoc.CreateFreeformDocRequest{ ProjectID: 7, Title: "超声探头规格.pdf", ContentMarkdown: "# 超声探头规格\n\n上传正文基线\n", Space: "my_private", ClientKey: "doc-upload-1789263145197-0", }) require.NoError(t, err) require.NotNil(t, detail) assert.True(t, len(detail.Document.DocID) > 0) assert.True(t, detail.Document.DocID[:8] == "doc-free" || detail.Document.DocID[:9] == "doc-free-") require.NotNil(t, detail.Document.ClientKey) assert.Equal(t, "doc-upload-1789263145197-0", *detail.Document.ClientKey) assert.Equal(t, "freeform", detail.Document.GovernanceMode) // source_content_sha256 must remain content fingerprint — never client_key prefix assert.NotContains(t, detail.Document.SourceContentSHA256, "doc-upload") assert.NotContains(t, detail.Document.SourceContentSHA256, "client_key") assert.Len(t, detail.Document.SourceContentSHA256, 64) st, err := rev.GetRevisionState(ctx, detail.Document.DocID) require.NoError(t, err) assert.GreaterOrEqual(t, st.CurrentVersionNo, 1) again, err := svc.CreateDocument(ctx, 42, freedoc.CreateFreeformDocRequest{ ProjectID: 7, Title: "超声探头规格.pdf", ContentMarkdown: "# 超声探头规格\n\n上传正文基线\n", ClientKey: "doc-upload-1789263145197-0", }) require.NoError(t, err) assert.Equal(t, detail.Document.DocID, again.Document.DocID) otherUser, err := svc.CreateDocument(ctx, 99, freedoc.CreateFreeformDocRequest{ ProjectID: 7, Title: "他人同名键", ContentMarkdown: "# other\n", ClientKey: "doc-upload-1789263145197-0", }) require.NoError(t, err) assert.NotEqual(t, detail.Document.DocID, otherUser.Document.DocID) } func TestCreateDocument_AcceptsSnowflakeProjectID(t *testing.T) { // projects.id is BIGINT; freeform must accept IDs above int4 max (regression: int4 encode 500). const snowflakeProjectID int64 = 1789095399489 svc := freedoc.NewService(nil, nil) detail, err := svc.CreateDocument(context.Background(), 42, freedoc.CreateFreeformDocRequest{ ProjectID: snowflakeProjectID, Title: "snowflake.pdf", ContentMarkdown: "# snowflake\n", Space: "my_private", }) require.NoError(t, err) assert.Equal(t, snowflakeProjectID, detail.Document.ProjectID) list, err := svc.ListDocuments(context.Background(), 42, snowflakeProjectID, "my_private") require.NoError(t, err) require.Len(t, list, 1) assert.Equal(t, snowflakeProjectID, list[0].ProjectID) } func TestCreateDocument_RejectsEmptyContent(t *testing.T) { svc := freedoc.NewService(nil, nil) _, err := svc.CreateDocument(context.Background(), 1, freedoc.CreateFreeformDocRequest{ ProjectID: 1, Title: "x", ContentMarkdown: " ", }) assert.ErrorIs(t, err, freedoc.ErrInvalidParam) } func TestCreateDocument_RequiresProjectIDAndPrivateSpace(t *testing.T) { svc := freedoc.NewService(nil, nil) _, err := svc.CreateDocument(context.Background(), 1, freedoc.CreateFreeformDocRequest{ ProjectID: 0, Title: "x", ContentMarkdown: "# x\n", }) assert.ErrorIs(t, err, freedoc.ErrInvalidParam) assert.Contains(t, err.Error(), "project_id") _, err = svc.CreateDocument(context.Background(), 1, freedoc.CreateFreeformDocRequest{ ProjectID: 1, Title: "x", ContentMarkdown: "# x\n", Space: "project_shared", }) assert.ErrorIs(t, err, freedoc.ErrInvalidParam) assert.Contains(t, err.Error(), "my_private") } func TestCreateDocument_PersistsSourceFileMetadata(t *testing.T) { ctx := context.Background() svc := freedoc.NewService(nil, nil) detail, err := svc.CreateDocument(ctx, 42, freedoc.CreateFreeformDocRequest{ ProjectID: 7, Title: "upload-note", ContentMarkdown: "# upload-note\n\n临床需求摘要占位\n", Space: "my_private", ClientKey: "doc-upload-meta-1", SourceFilename: "upload-note.txt", SourceMimeType: "text/plain", SourceSizeBytes: 41, SourceAssetID: 501, }) require.NoError(t, err) require.NotNil(t, detail) assert.Equal(t, "upload-note.txt", detail.Document.SourceFilename) assert.Equal(t, "text/plain", detail.Document.SourceMimeType) assert.EqualValues(t, 41, detail.Document.SourceSizeBytes) assert.EqualValues(t, 501, detail.Document.SourceAssetID) // 列表概要同样带出上传元数据(前端跨端恢复预览路由的事实源)。 list, err := svc.ListDocuments(ctx, 42, 7, "my_private") require.NoError(t, err) require.Len(t, list, 1) assert.Equal(t, "upload-note.txt", list[0].SourceFilename) assert.EqualValues(t, 41, list[0].SourceSizeBytes) assert.EqualValues(t, 501, list[0].SourceAssetID) // 非上传文档元数据恒为零值,不影响既有编辑器路径。 plain, err := svc.CreateDocument(ctx, 42, freedoc.CreateFreeformDocRequest{ ProjectID: 7, Title: "plain-note", ContentMarkdown: "# plain\n", ClientKey: "doc-upload-meta-2", }) require.NoError(t, err) assert.Empty(t, plain.Document.SourceFilename) assert.EqualValues(t, 0, plain.Document.SourceSizeBytes) } func TestCreateDocument_RejectsNegativeSourceMetadata(t *testing.T) { ctx := context.Background() svc := freedoc.NewService(nil, nil) _, err := svc.CreateDocument(ctx, 42, freedoc.CreateFreeformDocRequest{ ProjectID: 7, Title: "bad", ContentMarkdown: "# bad\n", SourceSizeBytes: -1, }) assert.ErrorIs(t, err, freedoc.ErrInvalidParam) _, err = svc.CreateDocument(ctx, 42, freedoc.CreateFreeformDocRequest{ ProjectID: 7, Title: "bad", ContentMarkdown: "# bad\n", SourceAssetID: -5, }) assert.ErrorIs(t, err, freedoc.ErrInvalidParam) }