Files
larksuite__cli/shortcuts/common/office_token_test.go
R0bynZhu 0f60fbfbdd fix(slides): relax office token length check from 28 to >=25 (#2531)
* fix(slides): relax office token length check from 28 to >=25

The interleaved "OFL0X" product/region marker is read at fixed positions
(1-based 5/10/15/20/25), so a token only has to be long enough to hold
it. Pinning the total length to exactly 28 silently reclassified every
other length as native.

28 is already stale: per #2509 the local-office format is "OFL0X + 21
random + 1 office type enum" = 27 characters, and sheets relaxed the
identical guard to >= 25 in that PR. Slides was missed, so an imported
office deck at the current length uploaded with parent_type "slide_file"
instead of "office_slide_file".

The marker positions are unchanged. Relaxing the length is only safe
because of them: a false positive is the dangerous direction, since the
drive backend does not validate that parent_node actually names an office
file, so a misclassified native deck uploads successfully and only
surfaces later as an image that will not render. The interleaved native
token cases (same length, different marker) are what keep the floor
honest.

Tests: replaced two mislabelled rows with five verified ones covering 27,
29, 25, 24 characters and a 28-character token carrying the ppt office
type enum. #2509's own labels were off by one or two characters and it
never covered the 25/24 boundary; this does.

* refactor(common): extract local-office token detection into common

The office token shape existed in three identical copies:
shortcuts/sheets/helpers.go, shortcuts/sheets/backward, and
shortcuts/slides. Every copy is somewhere a format change has to be found
again, and that is not hypothetical — #2509 had to apply the same
28-to->=25 relaxation twice inside sheets, and missed slides entirely.

Moved the shape to common.IsLocalOfficeToken. It belongs there because
recognising a local-office document is a drive-level property, not a
per-domain one: an imported office file is an imported office file whether
it backs a spreadsheet or a deck. What genuinely differs per domain is the
parent_type the answer selects — office_sheet_file vs office_slide_file —
so those mappings stay with each domain.

The name deliberately matches the vocabulary #2509 already used
("local-office format"). Its doc comment calls out that "local office" is
the whole category and not the LocalOfficeTokenPrefix case, since the two
now share a word stem while the predicate also accepts
FakeOfficeTokenPrefix and the interleaved marker.

Only slides is rewired here. The two sheets copies are left alone on
purpose to keep this reviewable as a pure no-op for them; they can follow
separately.

The marker offsets are now an array whose length is tied to the marker
string, so adding a character to one without the other stops compiling,
and TestOfficeTokenMinLenMatchesMarkerOffsets pins the length floor to one
past the last offset rather than letting the two merely agree by
coincidence.

Behaviour is unchanged, verified by diffing dry-run parent_type between
the pre-refactor and post-refactor binaries across 13 tokens covering both
prefixes, the 24/25 boundary, 27/28/29 characters, interleaved native
pptcn/shtcn markers, a leading-but-misaligned OFL0X, and an off-by-one
offset: 13/13 identical.

* refactor(sheets): route local-office detection through common

Deletes the last two copies of the token shape, both byte-identical to the
one now in common: shortcuts/sheets/helpers.go and
shortcuts/sheets/backward/lark_sheets_float_images.go. sheetMediaParentType
keeps owning the sheets half of the decision — which parent_type the answer
selects — and only the shape moves.

Equivalence was not assumed from reading. A throwaway fuzz test compared
isOfficeSpreadsheet against common.IsLocalOfficeToken in both packages over
an alphabet biased toward the characters that can actually disagree
(OFL0X plus the native product markers), every single-byte mutation of a
known office token at all 28 positions, and 400k fixed-seed random tokens
of length 0-33. Zero disagreements in either package. Dry-run parent_type
was then diffed binary-to-binary against origin/main across 11 tokens
covering the 24/25 boundary, 27/28/29 characters, interleaved native
shtcn/pptcn markers, a leading-but-misaligned OFL0X and an off-by-one
offset: sheets identical on all 11.

Two comment fixes that the extraction made unavoidable:

The const-block doc in both files still described "a 28-character token".
That was already wrong on main — #2509 relaxed the guard to >= 25 and left
the comment behind — and the shape is no longer described here at all now,
so both defer to common.IsLocalOfficeToken.

Four rows in TestSheetMediaParentType were mislabelled: "25 char, at
boundary" held a 27-character token and the three "new 27-char" rows held
28-character ones, so the floor those labels claimed to cover was never
tested. Relabelled by measured length, and the real 25/24 boundary added.
2026-08-28 10:39:04 +08:00

68 lines
3.0 KiB
Go

// Copyright (c) 2026 Lark Technologies Pte. Ltd.
// SPDX-License-Identifier: MIT
package common
import "testing"
// TestIsLocalOfficeToken pins the shared token shape every domain's
// parent_type mapping now reads through.
//
// The negative cases carry as much weight as the positive ones. A native token
// read as office still uploads successfully — the drive backend does not
// validate that parent_node names an office file — so the failure only shows up
// later as an image that will not render. The interleaved native tokens are what
// make the length floor safe: they are long enough to be read but carry a
// different marker, so relaxing the length must not pull them in.
func TestIsLocalOfficeToken(t *testing.T) {
t.Parallel()
cases := []struct {
name string
token string
want bool
}{
{"empty token", "", false},
{"native token, too short to read", "pptcnABC123", false},
{"fake_office prefix", "fake_office_abc123", true},
{"fake_office, only the prefix", FakeOfficeTokenPrefix, true},
{"local_office prefix", "local_office_abc123", true},
{"local_office, only the prefix", LocalOfficeTokenPrefix, true},
{"fake_office prefix mid-string is not a prefix", "pptfake_office_abc", false},
{"local_office prefix mid-string is not a prefix", "pptlocal_office_abc", false},
{"interleaved OFL0X, 25 chars (marker exactly fills the token)", "aaaaOaaaaFaaaaLaaaa0aaaaX", true},
{"interleaved OFL0X, 27 chars (current local-office format)", "aaaaOaaaaFaaaaLaaaa0aaaaXaa", true},
{"interleaved OFL0X, 28 chars (the length that used to be pinned)", "aaaaOaaaaFaaaaLaaaa0aaaaXaaa", true},
{"interleaved OFL0X, 28 chars with ppt office-type enum", "ccccOccccFccccLcccc0ccccXccP", true},
{"interleaved OFL0X, 29 chars (longer than any known format)", "aaaaOaaaaFaaaaLaaaa0aaaaXaaaa", true},
{"interleaved OFL0X, 24 chars (one short of holding the marker)", "aaaaOaaaaFaaaaLaaaa0aaaa", false},
{"interleaved pptcn native token", "abcdpefghpijkltmnopcqrstnuv", false},
{"interleaved shtcn native token", "abcdsefghhijkltmnopcqrstnuv", false},
{"OFL0X present but not on the offsets", "OFL0Xaaaaaaaaaaaaaaaaaaaaaaa", false},
{"marker off by one offset", "aaaaaOaaaaFaaaaLaaaa0aaaaX", false},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
if got := IsLocalOfficeToken(tc.token); got != tc.want {
t.Fatalf("IsLocalOfficeToken(%q) = %v, want %v", tc.token, got, tc.want)
}
})
}
}
// TestOfficeTokenMinLenMatchesMarkerOffsets keeps the length floor derived from
// the offsets rather than merely agreeing with them today. A floor that drifts
// above the last offset would reject valid short tokens; one that drifts below
// it would index out of range.
func TestOfficeTokenMinLenMatchesMarkerOffsets(t *testing.T) {
t.Parallel()
last := officeTokenMarkerOffsets[len(officeTokenMarkerOffsets)-1]
if officeTokenMinLen != last+1 {
t.Fatalf("officeTokenMinLen = %d, want %d (one past last marker offset %d)",
officeTokenMinLen, last+1, last)
}
}