mirror of
https://github.com/esengine/DeepSeek-Reasonix.git
synced 2026-11-07 10:45:59 -10:00
Merge pull request #14729 from chuiwenwei/fix/register-the-unguarded-wire-mirrors
fix(repolint): register the wire mirrors that had no guard
This commit is contained in:
3 files changed
+336
-3
No files matched your search
@@ -45,9 +45,13 @@ const tuiInboxFile = "internal/frontend/tui/client_inbox.go"
|
||||
// mirroredWireTypes are the Go types the desktop keeps a second, hand-written
|
||||
// copy of: the graph, whose consumers all fold the same deltas, and the sandbox
|
||||
// editor, which draws a security posture a field it cannot read would get
|
||||
// wrong. Declared and not inferred, for the reason the sensitive paths are: no
|
||||
// spelling tells a mirrored contract from a struct with json tags.
|
||||
var mirroredWireTypes = []wireMirror{
|
||||
// wrong. Declared and not inferred: no spelling tells a mirrored contract from
|
||||
// a struct with json tags. Two files only so neither outgrows the line ceiling.
|
||||
var mirroredWireTypes = slices.Concat(mirroredWireTypesHead, mirroredWireTypesTail)
|
||||
|
||||
// mirroredWireTypesHead is the table's first half; wireparity_table.go holds
|
||||
// the rest.
|
||||
var mirroredWireTypesHead = []wireMirror{
|
||||
{"internal/frontend/serve/workspace_files.go", "workspaceFile", tsWorkspaceFile, "WorkspaceFile"},
|
||||
{"internal/base/workspacefile/file.go", "Ref", tsWorkspaceFile, "FileRef"},
|
||||
{"internal/base/workspacefile/file.go", "Info", tsWorkspaceFile, "PreviewInfo"},
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
package main
|
||||
|
||||
// The page's port files this half of the table reads. The other half's constants
|
||||
// live in wireparity.go.
|
||||
const (
|
||||
tsAdjudicationFile = "desktop/frontend-next/src/port/adjudication.ts"
|
||||
tsAttachmentFile = "desktop/frontend-next/src/port/attachment.ts"
|
||||
tsBackupFile = "desktop/frontend-next/src/port/backup.ts"
|
||||
tsCompositionFile = "desktop/frontend-next/src/port/composition.ts"
|
||||
tsContextCompositionFile = "desktop/frontend-next/src/port/context_composition.ts"
|
||||
tsHookFile = "desktop/frontend-next/src/port/hook.ts"
|
||||
tsMarketFile = "desktop/frontend-next/src/port/market.ts"
|
||||
tsMemoryFile = "desktop/frontend-next/src/port/memory.ts"
|
||||
tsNetworkFile = "desktop/frontend-next/src/port/network.ts"
|
||||
tsSkillFile = "desktop/frontend-next/src/port/skill.ts"
|
||||
tsStorageFile = "desktop/frontend-next/src/port/storage.ts"
|
||||
tsUsageFile = "desktop/frontend-next/src/port/usage.ts"
|
||||
)
|
||||
|
||||
// mirroredWireTypesTail is the second half of the registry whose first half is
|
||||
// wireparity.go. It is a second file only because one table would take that
|
||||
// file past the repository's line ceiling; the checker reads the two as one
|
||||
// list, and every row is a page copy that agrees field for field with the Go
|
||||
// struct its handler actually writes.
|
||||
var mirroredWireTypesTail = []wireMirror{
|
||||
// account.ts: DeviceGrant is absent. /account/login writes a map literal
|
||||
// assembled from the grant, so the struct is not what the page reads, and a
|
||||
// guard over it would not see that response drift.
|
||||
|
||||
// adjudication
|
||||
{"internal/frontend/serve/adjudications.go", "adjudicationItem", tsAdjudicationFile, "AdjudicationEntry"},
|
||||
{"internal/frontend/serve/adjudications.go", "adjudicationView", tsAdjudicationFile, "Adjudications"},
|
||||
// attachment
|
||||
{"internal/frontend/serve/drop.go", "droppedRef", tsAttachmentFile, "DroppedRef"},
|
||||
// backup
|
||||
{"internal/ext/configbackup/snapshot.go", "CategoryInfo", tsBackupFile, "BackupCategoryInfo"},
|
||||
{"internal/platform/account/backup.go", "Backup", tsBackupFile, "BackupEntry"},
|
||||
{"internal/ext/configbackup/snapshot.go", "Omission", tsBackupFile, "BackupOmission"},
|
||||
{"internal/frontend/serve/backup.go", "createBackupRequest", tsBackupFile, "BackupCreateRequest"},
|
||||
{"internal/ext/configbackup/paths.go", "PathRef", tsBackupFile, "BackupPathRef"},
|
||||
{"internal/ext/configbackup/plan.go", "PlanItem", tsBackupFile, "BackupPlanItem"},
|
||||
{"internal/ext/configbackup/plan.go", "Plan", tsBackupFile, "BackupPlan"},
|
||||
{"internal/ext/configbackup/apply.go", "PluginRef", tsBackupFile, "BackupPluginRef"},
|
||||
// boundary
|
||||
{"internal/session/control/config_problem.go", "ConfigProblem", tsBoundaryFile, "ConfigProblem"},
|
||||
{"internal/session/control/subagent_steer_settings.go", "SubagentSteerSettings", tsBoundaryFile, "SubagentSteerSettings"},
|
||||
{"internal/session/control/peer_mail_send.go", "PeerView", tsBoundaryFile, "PeerView"},
|
||||
{"internal/session/control/peer_mail.go", "PeerHeld", tsBoundaryFile, "PeerHeld"},
|
||||
{"internal/session/control/peer_threads.go", "PeerThread", tsBoundaryFile, "PeerThread"},
|
||||
{"internal/session/control/peer_mail_send.go", "PeerReceipt", tsBoundaryFile, "PeerReceipt"},
|
||||
{"internal/runtime/persona/presets.go", "Preset", tsBoundaryFile, "PersonaPreset"},
|
||||
{"internal/session/control/persona_settings.go", "PersonaSettings", tsBoundaryFile, "PersonaSettings"},
|
||||
{"internal/contract/config/unconverted_prices.go", "UnconvertedPrice", tsBoundaryFile, "UnconvertedPrice"},
|
||||
{"internal/session/control/turn_narration_settings.go", "TurnNarrationSettings", tsBoundaryFile, "TurnNarrationSettings"},
|
||||
{"internal/session/control/web_access_settings.go", "WebAccessSettings", tsBoundaryFile, "WebAccessSettings"},
|
||||
{"internal/tools/websearch/searxng_probe.go", "SearxngProbe", tsBoundaryFile, "SearxngProbe"},
|
||||
{"internal/session/control/feedback_level_notice.go", "FeedbackLevelNoticeSettings", tsBoundaryFile, "FeedbackLevelNotices"},
|
||||
// composition
|
||||
{"internal/contract/agentcomposition/composition.go", "Definition", tsCompositionFile, "AgentComposition"},
|
||||
{"internal/state/agentpreset/catalog.go", "Catalog", tsCompositionFile, "AgentPresetCatalog"},
|
||||
// context_composition
|
||||
{"internal/contract/event/notice_payload.go", "ContextComposition", tsContextCompositionFile, "ContextComposition"},
|
||||
// feedback
|
||||
{"internal/platform/feedback/link.go", "LinkedInstall", tsFeedbackFile, "FeedbackLinkedInstall"},
|
||||
// hook
|
||||
{"internal/frontend/serve/hooks.go", "hookEntry", tsHookFile, "HookEntry"},
|
||||
{"internal/frontend/serve/hooks.go", "hookSource", tsHookFile, "HookSource"},
|
||||
{"internal/frontend/serve/hooks.go", "hookEventInfo", tsHookFile, "HookEventInfo"},
|
||||
{"internal/ext/hook/edit.go", "DryRunResult", tsHookFile, "HookDryRun"},
|
||||
// host.ts is the Electron preload bridge, not kernel wire: its Refusal and
|
||||
// ViewRect are answered by the shell, so neither is a mirror of a Go struct.
|
||||
// hub
|
||||
{"internal/frontend/serve/hub_contract.go", "RuntimeView", tsHubFile, "RuntimeView"},
|
||||
{"internal/platform/openwith/openwith.go", "Status", tsHubFile, "OpenWithTarget"},
|
||||
{"internal/frontend/serve/history_search.go", "historySearchHit", tsHubFile, "HistoryHit"},
|
||||
// look
|
||||
{"internal/frontend/serve/appearance.go", "wallpaperLayerView", tsLookFile, "WallpaperLayer"},
|
||||
{"internal/frontend/serve/appearance.go", "wallpaperAudioView", tsLookFile, "WallpaperAudioTrack"},
|
||||
// market
|
||||
// MarketList is absent: /market/packages assembles a map, so market.Page is not the writer.
|
||||
{"internal/frontend/serve/market.go", "marketInstalled", tsMarketFile, "MarketInstalled"},
|
||||
{"internal/ext/market/client.go", "Version", tsMarketFile, "MarketVersion"},
|
||||
{"internal/ext/market/cache.go", "CacheNote", tsMarketFile, "MarketCache"},
|
||||
{"internal/frontend/serve/market.go", "marketDetailView", tsMarketFile, "MarketDetail"},
|
||||
{"internal/ext/market/install.go", "Request", tsMarketFile, "MarketRequest"},
|
||||
{"internal/ext/market/publish.go", "Submission", tsMarketFile, "MarketSubmission"},
|
||||
{"internal/ext/market/publish.go", "Published", tsMarketFile, "MarketPublished"},
|
||||
// mcp
|
||||
// McpDraft is absent: /mcp/parse assembles a map of servers and risks.
|
||||
{"internal/frontend/serve/catalog.go", "mcpTool", tsMcpFile, "McpTool"},
|
||||
{"internal/session/control/capability_scope.go", "CapabilityScope", tsMcpFile, "CapabilityScope"},
|
||||
{"internal/frontend/serve/catalog.go", "draftServer", tsMcpFile, "McpDraftServer"},
|
||||
{"internal/frontend/serve/catalog.go", "draftRisk", tsMcpFile, "McpRisk"},
|
||||
{"internal/ext/plugin/install.go", "MCPInstallResult", tsMcpFile, "McpInstallResult"},
|
||||
// memory
|
||||
{"internal/frontend/serve/memory.go", "memoryEntry", tsMemoryFile, "MemoryEntry"},
|
||||
{"internal/frontend/serve/memory.go", "unreadableMemory", tsMemoryFile, "UnreadableMemory"},
|
||||
{"internal/frontend/serve/memory.go", "editable", tsMemoryFile, "MemoryEdit"},
|
||||
// model
|
||||
{"internal/session/control/slash.go", "SlashItem", tsModelFile, "CompletionItem"},
|
||||
{"internal/session/control/slash.go", "SlashArg", tsModelFile, "CompletionArg"},
|
||||
{"internal/session/control/complete.go", "Completion", tsModelFile, "Completion"},
|
||||
{"internal/frontend/serve/modelcaps.go", "modelPrice", tsModelFile, "ModelPrice"},
|
||||
// network
|
||||
{"internal/session/control/network.go", "NetworkSettings", tsNetworkFile, "NetworkSettings"},
|
||||
{"internal/base/netclient/diagnose.go", "Probe", tsNetworkFile, "NetworkProbe"},
|
||||
// plugin
|
||||
{"internal/frontend/serve/plugins.go", "pluginItem", tsPluginFile, "PluginItem"},
|
||||
{"internal/frontend/serve/plugins.go", "pluginHook", tsPluginFile, "PluginHook"},
|
||||
{"internal/frontend/serve/plugins.go", "pluginServer", tsPluginFile, "PluginServer"},
|
||||
{"internal/frontend/serve/plugins.go", "pluginRuntime", tsPluginFile, "PluginRuntime"},
|
||||
{"internal/ext/pluginpkg/pluginpkg.go", "CompatibilityIssue", tsPluginFile, "PluginSkipped"},
|
||||
{"internal/frontend/serve/plugins.go", "pluginView", tsPluginFile, "PluginPackage"},
|
||||
// port
|
||||
{"internal/session/control/input.go", "InvocationRequest", tsPortFile, "InvocationRequest"},
|
||||
{"internal/frontend/serve/tray.go", "TrayPrefs", tsPortFile, "TrayPrefs"},
|
||||
// provider
|
||||
{"internal/frontend/serve/provider_check.go", "providerModelCheckRequest", tsProviderFile, "ProviderModelCheckRequest"},
|
||||
{"internal/frontend/serve/provider_edit.go", "providerEditBody", tsProviderFile, "ProviderEdit"},
|
||||
{"internal/frontend/serve/provider_model_prices.go", "modelPriceView", tsProviderFile, "ModelRate"},
|
||||
{"internal/frontend/serve/provider_model_limits.go", "modelLimitsView", tsProviderFile, "ModelLimit"},
|
||||
{"internal/frontend/serve/provider_model_effort.go", "modelEffortView", tsProviderFile, "ModelEffort"},
|
||||
{"internal/frontend/serve/provider_setup.go", "providerSetupState", tsProviderFile, "ProviderSetup"},
|
||||
// remote
|
||||
{"internal/frontend/serve/hub_remote.go", "RemoteProbeRoute", tsRemoteFile, "RemoteRoute"},
|
||||
{"internal/frontend/serve/hub_remote.go", "RemoteProbe", tsRemoteFile, "RemoteProbe"},
|
||||
// session
|
||||
{"internal/state/checkpoint/types.go", "FileStage", tsSessionFile, "RewindFileStage"},
|
||||
{"internal/session/control/approval_orchestration.go", "PostureReport", tsSessionFile, "ApprovalDefault"},
|
||||
{"internal/frontend/serve/balance.go", "walletLine", tsSessionFile, "WalletLine"},
|
||||
{"internal/frontend/serve/balance.go", "walletBody", tsSessionFile, "WalletReading"},
|
||||
// share
|
||||
{"internal/frontend/serve/cloud_presence.go", "CloudControllerView", tsShareFile, "CloudDevice"},
|
||||
{"internal/frontend/serve/device_share.go", "CloudShareOffer", tsShareFile, "CloudShareOffer"},
|
||||
// shell
|
||||
{"internal/session/control/shell.go", "ShellOption", tsShellFile, "ShellOption"},
|
||||
{"internal/session/control/shell.go", "ShellSettings", tsShellFile, "ShellSettings"},
|
||||
// skill
|
||||
{"internal/frontend/serve/catalog.go", "skillEntry", tsSkillFile, "SkillEntry"},
|
||||
{"internal/ext/skillupdate/skillupdate.go", "Change", tsSkillFile, "SkillUpdateChange"},
|
||||
{"internal/ext/skillupdate/skillupdate.go", "Plan", tsSkillFile, "SkillUpdatePlan"},
|
||||
{"internal/frontend/serve/skill_source.go", "skillSource", tsSkillFile, "SkillSource"},
|
||||
// storage
|
||||
{"internal/frontend/serve/storage.go", "storageRoot", tsStorageFile, "StorageRoot"},
|
||||
{"internal/frontend/serve/storage.go", "storageMove", tsStorageFile, "StorageMove"},
|
||||
{"internal/frontend/serve/storage.go", "storageRefusal", tsStorageFile, "StorageRefusal"},
|
||||
{"internal/frontend/serve/storage.go", "storagePlan", tsStorageFile, "StoragePlan"},
|
||||
// usage
|
||||
{"internal/contract/pricing/money.go", "Money", tsUsageFile, "Money"},
|
||||
{"internal/state/stats/query.go", "DailyTokens", tsUsageFile, "UsageDay"},
|
||||
{"internal/state/stats/query.go", "HourlyTokens", tsUsageFile, "UsageHour"},
|
||||
{"internal/state/stats/query.go", "ModelUsage", tsUsageFile, "UsageModel"},
|
||||
{"internal/state/stats/query.go", "ProviderUsage", tsUsageFile, "UsageProvider"},
|
||||
{"internal/state/stats/query.go", "RangeStats", tsUsageFile, "UsageReport"},
|
||||
{"internal/state/stats/upload.go", "UploadDay", tsUsageFile, "UploadDay"},
|
||||
{"internal/state/stats/upload.go", "UploadProvider", tsUsageFile, "UploadProvider"},
|
||||
{"internal/state/stats/upload.go", "UploadStats", tsUsageFile, "UploadReport"},
|
||||
// wire
|
||||
{"internal/contract/eventwire/wire.go", "Bound", tsWireFile, "Bound"},
|
||||
{"internal/contract/provider/argument_normalization.go", "ArgumentNormalization", tsWireFile, "ArgumentNormalization"},
|
||||
{"internal/contract/pricing/money.go", "Money", tsWireFile, "Money"},
|
||||
{"internal/contract/eventwire/wire.go", "Via", tsWireFile, "Via"},
|
||||
{"internal/contract/eventwire/wire.go", "AskOption", tsWireFile, "AskOption"},
|
||||
{"internal/contract/eventwire/wire.go", "Ask", tsWireFile, "Ask"},
|
||||
{"internal/contract/eventwire/extension.go", "ExtensionStatus", tsWireFile, "ExtensionStatus"},
|
||||
{"internal/contract/eventwire/extension.go", "ExtensionKeyValue", tsWireFile, "ExtensionKeyValue"},
|
||||
{"internal/contract/eventwire/extension.go", "ExtensionActionRef", tsWireFile, "ExtensionActionRef"},
|
||||
{"internal/contract/eventwire/extension.go", "ExtensionCard", tsWireFile, "ExtensionCard"},
|
||||
{"internal/contract/eventwire/extension.go", "ExtensionFormField", tsWireFile, "ExtensionFormField"},
|
||||
{"internal/contract/eventwire/extension.go", "ExtensionForm", tsWireFile, "ExtensionForm"},
|
||||
{"internal/contract/eventwire/extension.go", "ExtensionPanel", tsWireFile, "ExtensionPanel"},
|
||||
{"internal/contract/eventwire/extension.go", "ExtensionNotification", tsWireFile, "ExtensionNotification"},
|
||||
{"internal/contract/eventwire/extension.go", "ExtensionView", tsWireFile, "ExtensionView"},
|
||||
{"internal/contract/eventwire/wire.go", "StreamAttempt", tsWireFile, "StreamAttempt"},
|
||||
{"internal/contract/eventwire/wire.go", "MemoryCitation", tsWireFile, "MemoryCitation"},
|
||||
{"internal/contract/eventwire/receipt.go", "CompletionReceipt", tsWireFile, "Receipt"},
|
||||
{"internal/contract/eventwire/receipt.go", "ReceiptChange", tsWireFile, "ReceiptChange"},
|
||||
{"internal/contract/eventwire/receipt.go", "ReceiptVerification", tsWireFile, "ReceiptVerification"},
|
||||
{"internal/contract/eventwire/receipt.go", "ReceiptGap", tsWireFile, "ReceiptGap"},
|
||||
// workspace
|
||||
{"internal/platform/gitstatus/gitstatus.go", "Change", tsWorkspaceFile, "WorkspaceChange"},
|
||||
{"internal/frontend/serve/workspace_listing.go", "workspaceListing", tsWorkspaceFile, "WorkspaceFiles"},
|
||||
// WorkspaceEntry is absent: no Go writer sends that pair; its fields only coincide with unrelated folder types.
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// treeRoot is the repository root from the package directory a test runs in.
|
||||
func treeRoot(t *testing.T) string {
|
||||
t.Helper()
|
||||
root, err := filepath.Abs(filepath.Join("..", ".."))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return root
|
||||
}
|
||||
|
||||
// The added half of the registry is the claim in its pull request: each row is
|
||||
// a page copy that agrees today with the struct its handler writes. A row that
|
||||
// names nothing, or two rows that name the same reader, would guard a pair the
|
||||
// tree does not have while the gate reported green.
|
||||
func TestAddedWireMirrorsResolveAndAreUnique(t *testing.T) {
|
||||
root := treeRoot(t)
|
||||
tsBodies := map[string]string{}
|
||||
headReaders := map[string]bool{}
|
||||
for _, m := range mirroredWireTypesHead {
|
||||
headReaders[m.tsFile+"."+m.tsType] = true
|
||||
}
|
||||
seenPair := map[string]bool{}
|
||||
seenReader := map[string]bool{}
|
||||
for _, m := range mirroredWireTypesTail {
|
||||
pair := m.goFile + "." + m.goType + " -> " + m.tsFile + "." + m.tsType
|
||||
reader := m.tsFile + "." + m.tsType
|
||||
if seenPair[pair] {
|
||||
t.Errorf("%s is declared twice", pair)
|
||||
}
|
||||
seenPair[pair] = true
|
||||
// A reader registered once is the point; the same writer may still back
|
||||
// two readers (the page keeps a copy per port file that carries it).
|
||||
if headReaders[reader] || seenReader[reader] {
|
||||
t.Errorf("%s returns a reader the registry already guards", pair)
|
||||
}
|
||||
seenReader[reader] = true
|
||||
|
||||
// The rows added here register data copies only: a test file, a mock or
|
||||
// a fixture would make the guard read a shape the wire never carries.
|
||||
if strings.HasSuffix(m.goFile, "_test.go") || strings.HasSuffix(m.tsFile, ".test.ts") ||
|
||||
strings.Contains(filepath.Base(m.tsFile), "mock") || strings.Contains(filepath.Base(m.tsFile), "fixture") {
|
||||
t.Errorf("%s names a test or fixture file", pair)
|
||||
continue
|
||||
}
|
||||
|
||||
src, err := parseSource(root, m.goFile)
|
||||
if err != nil || src == nil || src.file == nil {
|
||||
t.Errorf("%s names a Go file repolint cannot parse", pair)
|
||||
continue
|
||||
}
|
||||
goFields, ok := wireFieldNames(src.file, m.goType)
|
||||
if !ok {
|
||||
t.Errorf("%s names no Go struct %s in %s", pair, m.goType, m.goFile)
|
||||
continue
|
||||
}
|
||||
|
||||
body, ok := tsBodies[m.tsFile]
|
||||
if !ok {
|
||||
raw, err := os.ReadFile(filepath.Join(root, filepath.FromSlash(m.tsFile)))
|
||||
if err != nil {
|
||||
t.Errorf("%s names a TS file that cannot be read: %v", pair, err)
|
||||
continue
|
||||
}
|
||||
body = string(raw)
|
||||
tsBodies[m.tsFile] = body
|
||||
}
|
||||
tsFields, _, found := tsInterfaceFields(body, m.tsType)
|
||||
if !found {
|
||||
t.Errorf("%s names no TS interface %s in %s", pair, m.tsType, m.tsFile)
|
||||
continue
|
||||
}
|
||||
|
||||
// Exact today, not close: the row's justification is that the two sides
|
||||
// carry the same names, and a set that differs is a row for a follow-up.
|
||||
if !slices.Equal(sortedCopy(goFields), sortedCopy(tsFields)) {
|
||||
t.Errorf("%s is not exact: Go sends %v, TS reads %v", pair, sortedCopy(goFields), sortedCopy(tsFields))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func sortedCopy(names []string) []string {
|
||||
out := append([]string(nil), names...)
|
||||
slices.Sort(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// One newly guarded pair, with a field removed from the page's copy: the guard
|
||||
// has to name the field, or a reviewer cannot tell a drift from a stale row.
|
||||
func TestAddedWireMirrorDroppedFieldIsNamed(t *testing.T) {
|
||||
root := treeRoot(t)
|
||||
const pair = `{"internal/frontend/serve/backup.go", "createBackupRequest", tsBackupFile, "BackupCreateRequest"}`
|
||||
var m wireMirror
|
||||
for _, candidate := range mirroredWireTypesTail {
|
||||
if candidate.goFile == "internal/frontend/serve/backup.go" && candidate.goType == "createBackupRequest" &&
|
||||
candidate.tsFile == tsBackupFile && candidate.tsType == "BackupCreateRequest" {
|
||||
m = candidate
|
||||
break
|
||||
}
|
||||
}
|
||||
if m.tsType == "" {
|
||||
t.Fatalf("the registry no longer declares %s", pair)
|
||||
}
|
||||
|
||||
src, err := parseSource(root, m.goFile)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
goFields, ok := wireFieldNames(src.file, m.goType)
|
||||
if !ok {
|
||||
t.Fatalf("%s does not resolve", m.goType)
|
||||
}
|
||||
raw, err := os.ReadFile(filepath.Join(root, filepath.FromSlash(m.tsFile)))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
body := string(raw)
|
||||
fields, line, found := tsInterfaceFields(body, m.tsType)
|
||||
if !found || !slices.Contains(fields, "passphrase") {
|
||||
t.Fatalf("%s does not declare passphrase in the tree", m.tsType)
|
||||
}
|
||||
dropped := strings.Replace(body, " passphrase: string;\n", "", 1)
|
||||
if dropped == body {
|
||||
t.Fatal("the test could not find the field to drop")
|
||||
}
|
||||
got := wireParityFindings([]wireMirror{m}, map[string][]string{m.goFile + "." + m.goType: goFields},
|
||||
map[string]string{m.tsFile: dropped})
|
||||
if len(got) != 1 {
|
||||
t.Fatalf("findings = %+v, want exactly the dropped field", got)
|
||||
}
|
||||
if !strings.Contains(got[0].Msg, `sends "passphrase"`) || got[0].File != m.tsFile {
|
||||
t.Fatalf("finding = %s:%d %s: %s, want it to name passphrase against %s",
|
||||
got[0].File, got[0].Line, got[0].Rule, got[0].Msg, m.tsFile)
|
||||
}
|
||||
t.Logf("dropping passphrase reported: %s:%d %s", got[0].File, line, got[0].Msg)
|
||||
}
|
||||
Reference in new issue
Block a user