Bump the renderer to 3f25999 and bind documents to published profile versions
Why: Agent-Document-Renderer main publishes company.default@1.1.0 with a declarative TOC (spec 04, acceptance 10 §4 §7) and keeps 1.0.0 loadable by version. The fixed defaultProfileVersion "1.0.0" no longer matched LoadCompanyDefaultContext (1.1.0), so every delivery failed with a profile mismatch. New documents must take the latest published version, while documents that already bind a published version keep rendering with exactly that version, because published rules are immutable and old versions remain retrievable (spec 08). Evidence: go vet ./... ok; go test -count=1 ./... ok (all packages); go run ./internal/docgen/internal/genrendereridentity -check ok; scripts/agent-exec validate --repo . ok; scripts/dm governance check --repo . ok; gofmt -l clean; git diff --cached --check clean. New tests cover the default binding (1.1.0, TOC present), pinned 1.0.0 (no TOC) and 1.1.0 for both a request pin and a persisted-Markdown replay, and refusal of unknown versions and other profile IDs before any profile load. Impact: docgen resolves the profile version from the persisted Markdown binding or the request, defaults new documents to profiles.LatestCompanyDefaultVersion, and loads it through LoadCompanyDefaultVersionContext. The legacy migration comparison records the declared TOC as an intentional corporate-layout difference (RDR-T10-CMP-002) and keeps it out of the body visible-text dimension. RendererIdentity is regenerated for 3f259990b5d4. Refs: unified renderer plan Task 13; renderer PR #7 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Vendored
+1
-1
Submodule deps/agent-document-renderer updated: 1095077473...3f259990b5
@@ -62,6 +62,7 @@ type docxNumbering struct {
|
||||
|
||||
type docxComparison struct {
|
||||
Paragraphs []string `json:"paragraphs"`
|
||||
TOC []string `json:"toc"`
|
||||
PageSize map[string]string `json:"pageSize"`
|
||||
Margins map[string]string `json:"margins"`
|
||||
Headers int `json:"headers"`
|
||||
@@ -95,7 +96,7 @@ func compareLegacyAndShared(title string, source []byte, canonical model.Canonic
|
||||
if legacyErr != nil || sharedErr != nil {
|
||||
detail := fmt.Sprintf("legacy=%v shared=%v", legacyErr, sharedErr)
|
||||
for _, name := range []string{
|
||||
"visible-text", "page-layout", "header-footer", "node-bookmarks", "safe-links", "security-objects", "word-semantics",
|
||||
"visible-text", "table-of-contents", "page-layout", "header-footer", "node-bookmarks", "safe-links", "security-objects", "word-semantics",
|
||||
"word-numbering", "word-task-state", "word-thematic-rule", "word-table-alignment",
|
||||
} {
|
||||
receipt.Dimensions = append(receipt.Dimensions, ComparisonDimension{Name: name, Status: "unassessed", Detail: detail})
|
||||
@@ -115,6 +116,12 @@ func compareLegacyAndShared(title string, source []byte, canonical model.Canonic
|
||||
} else {
|
||||
receipt.Dimensions = append(receipt.Dimensions, compareExactDimension("visible-text", legacyParagraphs, sharedDOCX.Paragraphs, ""))
|
||||
}
|
||||
if len(legacyDOCX.TOC) == 0 && len(sharedDOCX.TOC) > 0 {
|
||||
receipt.Dimensions = append(receipt.Dimensions, intentionalDimension("table-of-contents", legacyDOCX.TOC, sharedDOCX.TOC,
|
||||
comparisonExceptionCorporate, "published organization default declares a table of contents"))
|
||||
} else {
|
||||
receipt.Dimensions = append(receipt.Dimensions, compareExactDimension("table-of-contents", legacyDOCX.TOC, sharedDOCX.TOC, ""))
|
||||
}
|
||||
|
||||
if equalJSON(legacyDOCX.PageSize, sharedDOCX.PageSize) {
|
||||
receipt.Dimensions = append(receipt.Dimensions, compareExactDimension("page-size", legacyDOCX.PageSize, sharedDOCX.PageSize, ""))
|
||||
@@ -501,6 +508,11 @@ func inspectComparisonDOCX(raw []byte) (docxComparison, error) {
|
||||
inParagraph, inText, inCell, inParagraphBorder := false, false, false, false
|
||||
paragraphLevel, paragraphNumID, cellAlignment := 0, "", ""
|
||||
paragraphRule := false
|
||||
// A TOC is its field's paragraphs plus an immediately preceding caption
|
||||
// paragraph excluded from the outline (outlineLvl 9).
|
||||
inInstruction, paragraphTOC, paragraphCaption := false, false, false
|
||||
fieldDepth, tocFieldDepth := 0, 0
|
||||
var pendingCaption []string
|
||||
var table [][]string
|
||||
var row []string
|
||||
for {
|
||||
@@ -518,10 +530,27 @@ func inspectComparisonDOCX(raw []byte) (docxComparison, error) {
|
||||
inParagraph = true
|
||||
paragraph.Reset()
|
||||
paragraphLevel, paragraphNumID, paragraphRule = 0, "", false
|
||||
paragraphTOC, paragraphCaption = tocFieldDepth > 0, false
|
||||
case "t":
|
||||
if inParagraph {
|
||||
inText = true
|
||||
}
|
||||
case "outlineLvl":
|
||||
if inParagraph && xmlAttributeValue(value.Attr, "val") == "9" {
|
||||
paragraphCaption = true
|
||||
}
|
||||
case "fldChar":
|
||||
switch xmlAttributeValue(value.Attr, "fldCharType") {
|
||||
case "begin":
|
||||
fieldDepth++
|
||||
case "end":
|
||||
if fieldDepth == tocFieldDepth {
|
||||
tocFieldDepth = 0
|
||||
}
|
||||
fieldDepth = max(fieldDepth-1, 0)
|
||||
}
|
||||
case "instrText":
|
||||
inInstruction = true
|
||||
case "numId":
|
||||
if inParagraph {
|
||||
paragraphNumID = xmlAttributeValue(value.Attr, "val")
|
||||
@@ -559,15 +588,35 @@ func inspectComparisonDOCX(raw []byte) (docxComparison, error) {
|
||||
if inText {
|
||||
paragraph.Write(value)
|
||||
}
|
||||
if inInstruction && tocFieldDepth == 0 && fieldDepth > 0 {
|
||||
if fields := strings.Fields(string(value)); len(fields) > 0 && strings.EqualFold(fields[0], "TOC") {
|
||||
tocFieldDepth, paragraphTOC = fieldDepth, inParagraph
|
||||
}
|
||||
}
|
||||
case xml.EndElement:
|
||||
switch value.Name.Local {
|
||||
case "t":
|
||||
inText = false
|
||||
case "instrText":
|
||||
inInstruction = false
|
||||
case "pBdr":
|
||||
inParagraphBorder = false
|
||||
case "p":
|
||||
text := normalizeComparisonText(paragraph.String())
|
||||
if text != "" {
|
||||
if paragraphTOC {
|
||||
result.TOC = append(result.TOC, pendingCaption...)
|
||||
pendingCaption = nil
|
||||
if text != "" {
|
||||
result.TOC = append(result.TOC, text)
|
||||
}
|
||||
inParagraph = false
|
||||
break
|
||||
}
|
||||
result.Paragraphs = append(result.Paragraphs, pendingCaption...)
|
||||
pendingCaption = nil
|
||||
if text != "" && paragraphCaption {
|
||||
pendingCaption = []string{text}
|
||||
} else if text != "" {
|
||||
result.Paragraphs = append(result.Paragraphs, text)
|
||||
}
|
||||
if paragraphNumID != "" {
|
||||
@@ -596,6 +645,7 @@ func inspectComparisonDOCX(raw []byte) (docxComparison, error) {
|
||||
}
|
||||
}
|
||||
}
|
||||
result.Paragraphs = append(result.Paragraphs, pendingCaption...)
|
||||
if len(numbered) > 0 {
|
||||
numbering, found := parts["word/numbering.xml"]
|
||||
if !found {
|
||||
|
||||
+51
-19
@@ -9,6 +9,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"slices"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -26,7 +27,6 @@ const (
|
||||
ManifestSchemaVersion = "backend-document-delivery/v1"
|
||||
AdapterIdentity = RendererIdentity + "/word-adapter-v1"
|
||||
defaultProfileID = "company.default"
|
||||
defaultProfileVersion = "1.0.0"
|
||||
defaultRenderCapacity = 4
|
||||
)
|
||||
|
||||
@@ -194,18 +194,15 @@ func Prepare(ctx context.Context, request Request) (PreparedDocument, error) {
|
||||
if err := validateWorkspaceRoute(request, parsed.Metadata); err != nil {
|
||||
return PreparedDocument{}, err
|
||||
}
|
||||
if parsed.Metadata.ProfileID != defaultProfileID || parsed.Metadata.ProfileVersion != defaultProfileVersion {
|
||||
return PreparedDocument{}, fmt.Errorf("%w: Markdown binds %s@%s, expected %s@%s", ErrProfileMismatch,
|
||||
parsed.Metadata.ProfileID, parsed.Metadata.ProfileVersion, defaultProfileID, defaultProfileVersion)
|
||||
if _, err := profileVersionFor(request, &parsed.Metadata); err != nil {
|
||||
return PreparedDocument{}, err
|
||||
}
|
||||
} else {
|
||||
if request.ProfileID != "" || request.ProfileVersion != "" {
|
||||
if request.ProfileID != defaultProfileID || request.ProfileVersion != defaultProfileVersion {
|
||||
return PreparedDocument{}, fmt.Errorf("%w: requested %s@%s, expected %s@%s", ErrProfileMismatch,
|
||||
request.ProfileID, request.ProfileVersion, defaultProfileID, defaultProfileVersion)
|
||||
}
|
||||
profileVersion, err := profileVersionFor(request, nil)
|
||||
if err != nil {
|
||||
return PreparedDocument{}, err
|
||||
}
|
||||
source, err := workspaceMarkdown(request, defaultProfileID, defaultProfileVersion)
|
||||
source, err := workspaceMarkdown(request, defaultProfileID, profileVersion)
|
||||
if err != nil {
|
||||
return PreparedDocument{}, err
|
||||
}
|
||||
@@ -231,14 +228,14 @@ func Prepare(ctx context.Context, request Request) (PreparedDocument, error) {
|
||||
}
|
||||
|
||||
type generatorDependencies struct {
|
||||
loadDefault func(context.Context) (profiles.Bundle, error)
|
||||
loadProfile func(context.Context, string) (profiles.Bundle, error)
|
||||
newRenderer func(*profile.AssetReader) render.Renderer
|
||||
legacyBuild func(string, string) ([]byte, error)
|
||||
}
|
||||
|
||||
func defaultGeneratorDependencies() generatorDependencies {
|
||||
return generatorDependencies{
|
||||
loadDefault: profiles.LoadCompanyDefaultContext,
|
||||
loadProfile: profiles.LoadCompanyDefaultVersionContext,
|
||||
newRenderer: func(assets *profile.AssetReader) render.Renderer { return word.NewAdapter(assets) },
|
||||
legacyBuild: legacyBuild,
|
||||
}
|
||||
@@ -274,6 +271,7 @@ func generateWithOptions(ctx context.Context, request Request, options RenderOpt
|
||||
return Delivery{}, fmt.Errorf("%w: Markdown is %d bytes, limit is %d", ErrResourceLimit, len(request.Markdown), limits.MaxMarkdownBytes)
|
||||
}
|
||||
var parsed markdownrenderer.Result
|
||||
var boundMetadata *markdownrenderer.Metadata
|
||||
materialized := bytes.HasPrefix(bytes.TrimSpace(request.Markdown), []byte("---"))
|
||||
if materialized {
|
||||
parsed, err = parseMarkdownContext(ctx, request.Markdown, false, limits)
|
||||
@@ -283,17 +281,20 @@ func generateWithOptions(ctx context.Context, request Request, options RenderOpt
|
||||
if err := validateWorkspaceRoute(request, parsed.Metadata); err != nil {
|
||||
return Delivery{}, err
|
||||
}
|
||||
boundMetadata = &parsed.Metadata
|
||||
}
|
||||
bundle, err := dependencies.loadDefault(ctx)
|
||||
boundVersion, err := profileVersionFor(request, boundMetadata)
|
||||
if err != nil {
|
||||
return Delivery{}, fmt.Errorf("docgen: load company default: %w", err)
|
||||
return Delivery{}, err
|
||||
}
|
||||
bundle, err := dependencies.loadProfile(ctx, boundVersion)
|
||||
if err != nil {
|
||||
return Delivery{}, fmt.Errorf("docgen: load company default %s: %w", boundVersion, err)
|
||||
}
|
||||
profileID, profileVersion := bundle.Snapshot.ProfileIdentity()
|
||||
if request.ProfileID != "" || request.ProfileVersion != "" {
|
||||
if request.ProfileID != profileID || request.ProfileVersion != profileVersion {
|
||||
return Delivery{}, fmt.Errorf("%w: requested %s@%s, available %s@%s", ErrProfileMismatch,
|
||||
request.ProfileID, request.ProfileVersion, profileID, profileVersion)
|
||||
}
|
||||
if profileID != defaultProfileID || profileVersion != boundVersion {
|
||||
return Delivery{}, fmt.Errorf("%w: loaded %s@%s, bound %s@%s", ErrProfileMismatch,
|
||||
profileID, profileVersion, defaultProfileID, boundVersion)
|
||||
}
|
||||
if !materialized {
|
||||
source, sourceErr := workspaceMarkdown(request, profileID, profileVersion)
|
||||
@@ -481,6 +482,37 @@ func validateWorkspaceRoute(request Request, metadata markdownrenderer.Metadata)
|
||||
return nil
|
||||
}
|
||||
|
||||
// profileVersionFor returns the company-default version a document renders
|
||||
// with. Published versions are immutable, so a version the persisted Markdown
|
||||
// or the request already binds is kept; only a new, unbound document takes the
|
||||
// latest published version. metadata is nil until Markdown is materialized.
|
||||
func profileVersionFor(request Request, metadata *markdownrenderer.Metadata) (string, error) {
|
||||
requested := request.ProfileID != "" || request.ProfileVersion != ""
|
||||
if requested && !publishedProfileVersion(request.ProfileID, request.ProfileVersion) {
|
||||
return "", fmt.Errorf("%w: requested %s@%s is not a published %s version", ErrProfileMismatch,
|
||||
request.ProfileID, request.ProfileVersion, defaultProfileID)
|
||||
}
|
||||
if metadata == nil {
|
||||
if requested {
|
||||
return request.ProfileVersion, nil
|
||||
}
|
||||
return profiles.LatestCompanyDefaultVersion, nil
|
||||
}
|
||||
if !publishedProfileVersion(metadata.ProfileID, metadata.ProfileVersion) {
|
||||
return "", fmt.Errorf("%w: Markdown binds %s@%s, which is not a published %s version", ErrProfileMismatch,
|
||||
metadata.ProfileID, metadata.ProfileVersion, defaultProfileID)
|
||||
}
|
||||
if requested && request.ProfileVersion != metadata.ProfileVersion {
|
||||
return "", fmt.Errorf("%w: requested %s@%s, Markdown binds %s@%s", ErrProfileMismatch,
|
||||
request.ProfileID, request.ProfileVersion, metadata.ProfileID, metadata.ProfileVersion)
|
||||
}
|
||||
return metadata.ProfileVersion, nil
|
||||
}
|
||||
|
||||
func publishedProfileVersion(profileID, profileVersion string) bool {
|
||||
return profileID == defaultProfileID && slices.Contains(profiles.CompanyDefaultVersions(), profileVersion)
|
||||
}
|
||||
|
||||
func validateWorkspaceMetadata(request Request, metadata markdownrenderer.Metadata, profileID, profileVersion string) error {
|
||||
if err := validateWorkspaceRoute(request, metadata); err != nil {
|
||||
return err
|
||||
|
||||
@@ -2,6 +2,6 @@
|
||||
|
||||
package docgen
|
||||
|
||||
const rendererRevision = "10950774736b64b5d8e544ce03d45ae5a6ceca7c"
|
||||
const rendererRevision = "3f259990b5d43ec0d318f39d496816140ab559b5"
|
||||
|
||||
const RendererIdentity = "agent-document-renderer@" + rendererRevision
|
||||
|
||||
@@ -160,7 +160,7 @@ func TestGeneratePersistsCanonicalMarkdownWithStableNodeParity(t *testing.T) {
|
||||
if reparsed.Metadata.DocumentID != "workspace-minutes" || reparsed.Metadata.ProjectID != WorkspaceProjectID ||
|
||||
reparsed.Metadata.DocumentType != "general" || reparsed.Metadata.Title != "研发周会纪要" ||
|
||||
reparsed.Metadata.Language != "zh-CN" || reparsed.Metadata.ProfileID != "company.default" ||
|
||||
reparsed.Metadata.ProfileVersion != "1.0.0" || reparsed.Metadata.Controlled {
|
||||
reparsed.Metadata.ProfileVersion != profiles.LatestCompanyDefaultVersion || reparsed.Metadata.Controlled {
|
||||
t.Fatalf("controlled metadata changed: %#v", reparsed.Metadata)
|
||||
}
|
||||
|
||||
@@ -355,10 +355,10 @@ func TestPersistedControlledMetadataRoutesBeforeDefaultProfileLoad(t *testing.T)
|
||||
request.Title = "Controlled"
|
||||
loads := 0
|
||||
deps := defaultGeneratorDependencies()
|
||||
baseLoad := deps.loadDefault
|
||||
deps.loadDefault = func(ctx context.Context) (profiles.Bundle, error) {
|
||||
baseLoad := deps.loadProfile
|
||||
deps.loadProfile = func(ctx context.Context, version string) (profiles.Bundle, error) {
|
||||
loads++
|
||||
return baseLoad(ctx)
|
||||
return baseLoad(ctx, version)
|
||||
}
|
||||
_, err := generate(context.Background(), request, deps)
|
||||
if !errors.Is(err, ErrRDMRequired) || loads != 0 {
|
||||
@@ -367,7 +367,7 @@ func TestPersistedControlledMetadataRoutesBeforeDefaultProfileLoad(t *testing.T)
|
||||
}
|
||||
|
||||
func TestRendererIdentityMatchesPublishedPin(t *testing.T) {
|
||||
const want = "agent-document-renderer@10950774736b64b5d8e544ce03d45ae5a6ceca7c"
|
||||
const want = "agent-document-renderer@3f259990b5d43ec0d318f39d496816140ab559b5"
|
||||
if RendererIdentity != want {
|
||||
t.Fatalf("RendererIdentity = %q, want %q", RendererIdentity, want)
|
||||
}
|
||||
@@ -456,6 +456,157 @@ func TestGenerateRejectsProfileAndAssetMismatch(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
const tocFieldInstruction = `TOC \o "1-`
|
||||
|
||||
func TestGenerateBindsNewDocumentsToLatestPublishedProfile(t *testing.T) {
|
||||
request := workspaceRequest([]byte(representativeMarkdown))
|
||||
prepared, err := Prepare(context.Background(), request)
|
||||
if err != nil {
|
||||
t.Fatalf("Prepare: %v", err)
|
||||
}
|
||||
delivery, err := Generate(context.Background(), request)
|
||||
if err != nil {
|
||||
t.Fatalf("Generate: %v", err)
|
||||
}
|
||||
latest, err := profiles.LoadCompanyDefault()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, version := latest.Snapshot.ProfileIdentity(); version != profiles.LatestCompanyDefaultVersion {
|
||||
t.Fatalf("renderer latest company default = %s, want %s", version, profiles.LatestCompanyDefaultVersion)
|
||||
}
|
||||
for name, markdown := range map[string][]byte{"Prepare": prepared.Markdown.Bytes, "Generate": delivery.Markdown.Bytes} {
|
||||
metadata := persistedMetadata(t, markdown)
|
||||
if metadata.ProfileID != "company.default" || metadata.ProfileVersion != profiles.LatestCompanyDefaultVersion {
|
||||
t.Fatalf("%s bound new document to %s@%s, want company.default@%s", name, metadata.ProfileID, metadata.ProfileVersion, profiles.LatestCompanyDefaultVersion)
|
||||
}
|
||||
}
|
||||
if delivery.Profile.Version != profiles.LatestCompanyDefaultVersion || delivery.Profile.SnapshotHash != latest.Snapshot.SnapshotHash() {
|
||||
t.Fatalf("delivery profile receipt = %#v, want latest snapshot %s", delivery.Profile, latest.Snapshot.SnapshotHash())
|
||||
}
|
||||
documentXML := docxPart(t, delivery.DOCX.Bytes, "word/document.xml")
|
||||
if !strings.Contains(documentXML, tocFieldInstruction) || !strings.Contains(docxVisibleText(t, delivery.DOCX.Bytes), "目录") {
|
||||
t.Fatal("new document rendered with the latest company default omitted its declared TOC")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateKeepsPinnedPublishedProfileVersion(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
version string
|
||||
toc bool
|
||||
}{
|
||||
{version: "1.0.0", toc: false},
|
||||
{version: "1.1.0", toc: true},
|
||||
} {
|
||||
t.Run(test.version, func(t *testing.T) {
|
||||
pinned, err := profiles.LoadCompanyDefaultVersion(test.version)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
request := workspaceRequest([]byte(representativeMarkdown))
|
||||
request.ProfileID, request.ProfileVersion = "company.default", test.version
|
||||
prepared, err := Prepare(context.Background(), request)
|
||||
if err != nil {
|
||||
t.Fatalf("Prepare pinned %s: %v", test.version, err)
|
||||
}
|
||||
if metadata := persistedMetadata(t, prepared.Markdown.Bytes); metadata.ProfileVersion != test.version {
|
||||
t.Fatalf("Prepare bound pinned request to %s, want %s", metadata.ProfileVersion, test.version)
|
||||
}
|
||||
first, err := Generate(context.Background(), request)
|
||||
if err != nil {
|
||||
t.Fatalf("Generate pinned %s: %v", test.version, err)
|
||||
}
|
||||
|
||||
replay := workspaceRequest(first.Markdown.Bytes)
|
||||
second, err := Generate(context.Background(), replay)
|
||||
if err != nil {
|
||||
t.Fatalf("Generate persisted %s Markdown: %v", test.version, err)
|
||||
}
|
||||
for name, delivery := range map[string]Delivery{"pinned request": first, "persisted replay": second} {
|
||||
if delivery.Profile.ProfileID != "company.default" || delivery.Profile.Version != test.version ||
|
||||
delivery.Profile.SnapshotHash != pinned.Snapshot.SnapshotHash() || delivery.Profile.ProfileSHA256 != pinned.Review.ProfileSHA256 {
|
||||
t.Fatalf("%s profile receipt = %#v, want company.default@%s snapshot %s", name, delivery.Profile, test.version, pinned.Snapshot.SnapshotHash())
|
||||
}
|
||||
if metadata := persistedMetadata(t, delivery.Markdown.Bytes); metadata.ProfileVersion != test.version {
|
||||
t.Fatalf("%s Markdown binds %s, want %s", name, metadata.ProfileVersion, test.version)
|
||||
}
|
||||
if got := strings.Contains(docxPart(t, delivery.DOCX.Bytes, "word/document.xml"), tocFieldInstruction); got != test.toc {
|
||||
t.Fatalf("%s TOC present = %v, want %v for company.default@%s", name, got, test.toc, test.version)
|
||||
}
|
||||
}
|
||||
if !bytes.Equal(first.DOCX.Bytes, second.DOCX.Bytes) {
|
||||
t.Fatal("replaying persisted Markdown changed the pinned delivery bytes")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestProfileBindingRefusesUnpublishedVersionsBeforeLoad(t *testing.T) {
|
||||
persisted, err := Generate(context.Background(), workspaceRequest([]byte(representativeMarkdown)))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
latestBinding := []byte("profileVersion: \"" + profiles.LatestCompanyDefaultVersion + "\"\n")
|
||||
if !bytes.Contains(persisted.Markdown.Bytes, latestBinding) {
|
||||
t.Fatalf("persisted Markdown does not carry %q", latestBinding)
|
||||
}
|
||||
pinnedOld := workspaceRequest(bytes.Replace(persisted.Markdown.Bytes, latestBinding, []byte("profileVersion: \"1.0.0\"\n"), 1))
|
||||
pinnedOld.ProfileID, pinnedOld.ProfileVersion = "company.default", profiles.LatestCompanyDefaultVersion
|
||||
|
||||
tests := map[string]Request{}
|
||||
for name, identity := range map[string][2]string{
|
||||
"unknown version": {"company.default", "9.9.9"},
|
||||
"other profile": {"company.other", "1.0.0"},
|
||||
"missing version": {"company.default", ""},
|
||||
"missing profile id": {"", "1.0.0"},
|
||||
} {
|
||||
request := workspaceRequest([]byte(representativeMarkdown))
|
||||
request.ProfileID, request.ProfileVersion = identity[0], identity[1]
|
||||
tests["requested "+name] = request
|
||||
}
|
||||
tests["persisted unknown version"] = workspaceRequest(bytes.Replace(persisted.Markdown.Bytes, latestBinding, []byte("profileVersion: \"9.9.9\"\n"), 1))
|
||||
tests["persisted other profile"] = workspaceRequest(bytes.Replace(persisted.Markdown.Bytes,
|
||||
[]byte("profileId: \"company.default\"\n"), []byte("profileId: \"company.other\"\n"), 1))
|
||||
tests["request disagrees with persisted binding"] = pinnedOld
|
||||
|
||||
for name, request := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
if _, err := Prepare(context.Background(), request); !errors.Is(err, ErrProfileMismatch) {
|
||||
t.Fatalf("Prepare error = %v, want ErrProfileMismatch", err)
|
||||
}
|
||||
loads := 0
|
||||
deps := defaultGeneratorDependencies()
|
||||
baseLoad := deps.loadProfile
|
||||
deps.loadProfile = func(ctx context.Context, version string) (profiles.Bundle, error) {
|
||||
loads++
|
||||
return baseLoad(ctx, version)
|
||||
}
|
||||
if _, err := generate(context.Background(), request, deps); !errors.Is(err, ErrProfileMismatch) || loads != 0 {
|
||||
t.Fatalf("Generate error = %v profileLoads=%d, want ErrProfileMismatch before load", err, loads)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateRefusesLoadedProfileWithUnexpectedIdentity(t *testing.T) {
|
||||
deps := defaultGeneratorDependencies()
|
||||
deps.loadProfile = func(ctx context.Context, _ string) (profiles.Bundle, error) {
|
||||
return profiles.LoadCompanyDefaultVersionContext(ctx, "1.0.0")
|
||||
}
|
||||
if _, err := generate(context.Background(), workspaceRequest([]byte(representativeMarkdown)), deps); !errors.Is(err, ErrProfileMismatch) {
|
||||
t.Fatalf("Generate with a loader returning the wrong version error = %v, want ErrProfileMismatch", err)
|
||||
}
|
||||
}
|
||||
|
||||
func persistedMetadata(t *testing.T, markdown []byte) markdownrenderer.Metadata {
|
||||
t.Helper()
|
||||
parsed, err := markdownrenderer.Parse(markdown, markdownrenderer.Options{MaxBytes: 2 << 20, MaxNodes: 10_000, MaxDepth: 64})
|
||||
if err != nil {
|
||||
t.Fatalf("persisted Markdown is not parseable: %v", err)
|
||||
}
|
||||
return parsed.Metadata
|
||||
}
|
||||
|
||||
func TestGenerateEnforcesLimitsAndCancellation(t *testing.T) {
|
||||
request := workspaceRequest([]byte(representativeMarkdown))
|
||||
request.Limits.MaxMarkdownBytes = 32
|
||||
@@ -713,6 +864,45 @@ func TestRepresentativeLegacyComparisonRecordsOnlyIntentionalDifferences(t *test
|
||||
t.Errorf("corporate layout exception %s detail = %q, want published organization default", dimension.Name, dimension.Detail)
|
||||
}
|
||||
}
|
||||
if dimension := comparisonDimension(t, delivery.Comparison, "table-of-contents"); dimension.Status != "intentional" || dimension.ExceptionID != comparisonExceptionCorporate {
|
||||
t.Errorf("declared TOC comparison = %#v, want intentional %s", dimension, comparisonExceptionCorporate)
|
||||
}
|
||||
if dimension := comparisonDimension(t, delivery.Comparison, "visible-text"); dimension.Status == "accidental" {
|
||||
t.Errorf("declared TOC leaked into the body visible-text comparison: %#v", dimension)
|
||||
}
|
||||
|
||||
request.ProfileID, request.ProfileVersion = "company.default", "1.0.0"
|
||||
pinned, err := generateWithOptions(context.Background(), request, RenderOptions{MigrationComparison: true}, defaultGeneratorDependencies())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if pinned.Comparison.Assessment != "assessed" || len(pinned.Comparison.AccidentalDifferences) != 0 {
|
||||
t.Fatalf("pinned 1.0.0 comparison has accidental differences: %#v", pinned.Comparison.AccidentalDifferences)
|
||||
}
|
||||
if dimension := comparisonDimension(t, pinned.Comparison, "table-of-contents"); dimension.Status != "match" {
|
||||
t.Errorf("pinned 1.0.0 TOC comparison = %#v, want match without a TOC", dimension)
|
||||
}
|
||||
}
|
||||
|
||||
func TestComparisonKeepsBodyTextLossAccidentalBesideDeclaredTOC(t *testing.T) {
|
||||
const source = "# Report\n\n## Scope\n\nScope body.\n\n## Result\n\nResult body.\n"
|
||||
request := workspaceRequest([]byte(source))
|
||||
request.Title = "Report"
|
||||
delivery, err := Generate(context.Background(), request)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
legacy, err := legacyBuild(request.Title, "# Report\n\n## Scope\n\nScope body.\n\n## Result\n\nDifferent body.\n")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
receipt := compareLegacyAndShared(request.Title, []byte(source), delivery.Document, legacy, delivery.DOCX.Bytes)
|
||||
if dimension := comparisonDimension(t, receipt, "table-of-contents"); dimension.Status != "intentional" {
|
||||
t.Fatalf("declared TOC comparison = %#v, want intentional", dimension)
|
||||
}
|
||||
if dimension := comparisonDimension(t, receipt, "visible-text"); dimension.Status != "accidental" {
|
||||
t.Fatalf("body text loss beside a TOC = %#v, want accidental", dimension)
|
||||
}
|
||||
}
|
||||
|
||||
func TestComparisonDetectsCanonicalAndDOCXTableAlignmentLoss(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user