From cb19b295fcc2de870f660be0f98ce62c39e3945d Mon Sep 17 00:00:00 2001 From: Alex Wilkerson John Date: Mon, 27 Jul 2026 21:58:09 -0400 Subject: [PATCH] fix: reduce high-volume precision false positives --- .../checks/design/local_abstraction.go | 32 +++- .../checks/quality/quality_precision.go | 110 +----------- .../quality/quality_precision_duplication.go | 159 ++++++++++++++++++ .../quality_precision_ui_conventions.go | 50 +++++- .../quality_precision_workstreams_cd.go | 5 +- .../checks/quality/quality_smells.go | 21 ++- .../checks/quality/quality_smells_ui.go | 38 +++++ tests/checks/design_local_abstraction_test.go | 29 ++++ .../function_hidden_mutation_noise_test.go | 47 ++++++ tests/checks/function_precision_test.go | 28 +++ ...uality_ui_false_positive_hardening_test.go | 99 ++++++++++- 11 files changed, 486 insertions(+), 132 deletions(-) create mode 100644 internal/codeguard/checks/quality/quality_precision_duplication.go diff --git a/internal/codeguard/checks/design/local_abstraction.go b/internal/codeguard/checks/design/local_abstraction.go index a468f79..9eeede6 100644 --- a/internal/codeguard/checks/design/local_abstraction.go +++ b/internal/codeguard/checks/design/local_abstraction.go @@ -117,18 +117,23 @@ func leakFindings(env support.Context, file string, source string) []core.Findin if trimmed == "" || strings.HasPrefix(trimmed, "//") || strings.HasPrefix(trimmed, "#") { continue } + codeLine := stripInlineDesignComment(trimmed) + if codeLine == "" { + continue + } lineNo := idx + 1 - if domainPath && infraLeakPattern.MatchString(trimmed) { + if domainPath && infraLeakPattern.MatchString(codeLine) { findings = append(findings, designFinding(env, ruleInfrastructureLeak, file, lineNo, "infrastructure/framework type leaks into a domain or public boundary", core.ConfidenceHigh)) } - if persistenceBoundaryPath && (apiPath || handlerPath || isPublicDeclaration(trimmed)) && persistenceLeakPattern.MatchString(trimmed) && - !allowedGeneratedPersistenceEnumLine(trimmed) && !allowedTypeScriptRecordUtilityLine(trimmed) && - !allowedUIPropsDerivedTypeLine(file, trimmed) && !allowedFrameworkDTOBoundaryLine(file, trimmed) { + if persistenceBoundaryPath && !isPackageAPIImplementationPath(file) && (apiPath || handlerPath || isPublicDeclaration(codeLine)) && + persistenceLeakPattern.MatchString(codeLine) && + !allowedGeneratedPersistenceEnumLine(codeLine) && !allowedTypeScriptRecordUtilityLine(codeLine) && + !allowedUIPropsDerivedTypeLine(file, codeLine) && !allowedFrameworkDTOBoundaryLine(file, codeLine) { findings = append(findings, designFinding(env, rulePersistenceLeak, file, lineNo, - fmt.Sprintf("persistence model or ORM concept leaks through boundary at %s:%d: %s", file, lineNo, findingLineExcerpt(trimmed)), core.ConfidenceHigh)) + fmt.Sprintf("persistence model or ORM concept leaks through boundary at %s:%d: %s", file, lineNo, findingLineExcerpt(codeLine)), core.ConfidenceHigh)) } - if domainPath && configLeakPattern.MatchString(trimmed) { + if domainPath && configLeakPattern.MatchString(codeLine) { findings = append(findings, designFinding(env, ruleConfigurationLeak, file, lineNo, "configuration or environment concern leaks into domain code", core.ConfidenceMedium)) } @@ -481,6 +486,11 @@ func isAPIPath(file string) bool { return strings.Contains(normalized, "/contract/") } +func isPackageAPIImplementationPath(file string) bool { + normalized := strings.ToLower(filepathSlash(file)) + return strings.Contains(normalized, "/packages/api/src/") || strings.HasPrefix(normalized, "packages/api/src/") +} + func isContractBoundaryPath(file string) bool { normalized := strings.ToLower(filepathSlash(file)) if isFrontendUIPath(file) { @@ -522,6 +532,16 @@ func filepathSlash(path string) string { return strings.ReplaceAll(path, "\\", "/") } +func stripInlineDesignComment(line string) string { + if idx := strings.Index(line, "//"); idx >= 0 { + line = line[:idx] + } + if idx := strings.Index(line, "#"); idx >= 0 { + line = line[:idx] + } + return strings.TrimSpace(line) +} + func isPublicDeclaration(line string) bool { return strings.HasPrefix(line, "export ") || strings.HasPrefix(line, "public ") || strings.HasPrefix(line, "func ") || strings.HasPrefix(line, "type ") || diff --git a/internal/codeguard/checks/quality/quality_precision.go b/internal/codeguard/checks/quality/quality_precision.go index 7354765..f63d815 100644 --- a/internal/codeguard/checks/quality/quality_precision.go +++ b/internal/codeguard/checks/quality/quality_precision.go @@ -7,7 +7,6 @@ import ( "go/printer" "go/token" "regexp" - "strconv" "strings" "github.com/devr-tools/codeguard/internal/codeguard/checks/support" @@ -469,7 +468,7 @@ func primitiveObsession(fn precisionFunction) bool { } func hiddenSideEffect(file string, fn precisionFunction) bool { - if isFrameworkOrchestrationBoundary(file, fn) { + if isFrameworkOrchestrationBoundary(file, fn) || isReactComponentOrNamedHookBoundary(file, fn) || explicitMutationName(fn.Name) { return false } if !queryFunctionPrefixPattern.MatchString(strings.ToLower(fn.Name)) { @@ -511,7 +510,7 @@ func isDomainLevelCall(callee string) bool { } func commandQueryMix(file string, fn precisionFunction) bool { - if isFrameworkOrchestrationBoundary(file, fn) || isReactComponentOrHookBoundary(file, fn) { + if isFrameworkOrchestrationBoundary(file, fn) || isReactComponentOrNamedHookBoundary(file, fn) || explicitMutationName(fn.Name) { return false } if !fn.Returns { @@ -631,23 +630,6 @@ func parsedMutableGlobalFindings(env support.Context, file string, parsed *suppo return findings } -func parsedDuplicatedKnowledgeFindings(env support.Context, file string, parsed *support.ParsedFile) []core.Finding { - if isQualityFixturePath(file) { - return nil - } - seen := map[string]int{} - for _, statement := range parsed.Module.Statements { - for _, literal := range domainKnowledgeLiterals(statement.Raw) { - if first, exists := seen[literal]; exists { - return []core.Finding{precisionWarnFinding(env, qualityDuplicatedKnowledgeRuleID, file, statement.Line, - fmt.Sprintf("business literal %s is duplicated near line %d; centralize shared domain knowledge", literal, first), core.ConfidenceLow)} - } - seen[literal] = statement.Line - } - } - return nil -} - func redundantCommentFindings(env support.Context, file string, source string) []core.Finding { if isQualityFixturePath(file) { return nil @@ -689,26 +671,6 @@ func sourceMutableGlobalFindings(env support.Context, file string, source string return nil } -func sourceDuplicatedKnowledgeFindings(env support.Context, file string, source string) []core.Finding { - if isQualityFixturePath(file) { - return nil - } - seen := map[string]int{} - for idx, line := range strings.Split(strings.ReplaceAll(source, "\r\n", "\n"), "\n") { - if strings.TrimSpace(line) == "" { - continue - } - for _, literal := range domainKnowledgeLiterals(line) { - if first, exists := seen[literal]; exists { - return []core.Finding{precisionWarnFinding(env, qualityDuplicatedKnowledgeRuleID, file, idx+1, - fmt.Sprintf("business literal %s is duplicated near line %d; centralize shared domain knowledge", literal, first), core.ConfidenceLow)} - } - seen[literal] = idx + 1 - } - } - return nil -} - func redundantCommentVerb(comment string) string { match := redundantCommentPattern.FindStringSubmatch(comment) if len(match) < 3 { @@ -717,74 +679,6 @@ func redundantCommentVerb(comment string) string { return strings.ToLower(match[2]) } -func domainKnowledgeLiterals(line string) []string { - if duplicatedKnowledgeLineIsDisplayOnly(line) { - return nil - } - matches := regexp.MustCompile(`"([^"]{2,80})"|'([^']{2,80})'|\b\d+(?:\.\d+)?\b`).FindAllString(line, -1) - out := make([]string, 0, len(matches)) - for _, match := range matches { - if domainKnowledgeLiteral(match) { - out = append(out, match) - } - } - return out -} - -func duplicatedKnowledgeLineIsDisplayOnly(line string) bool { - lowered := strings.ToLower(line) - if strings.Contains(lowered, "classname") || strings.Contains(lowered, "clasname") || strings.Contains(lowered, "class:") { - return true - } - if strings.Contains(line, "<") && strings.Contains(line, ">") { - return true - } - if strings.Contains(lowered, "label:") || strings.Contains(lowered, "placeholder:") || strings.Contains(lowered, "title:") || - strings.Contains(lowered, "aria-label") { - return true - } - return false -} - -func domainKnowledgeLiteral(value string) bool { - trimmed := strings.Trim(value, `"'`) - if trimmed == "" || len(trimmed) > 80 { - return false - } - if len(trimmed) < 4 && !strings.ContainsAny(trimmed, "0123456789") { - return false - } - if numeric, ok := duplicatedKnowledgeNumber(trimmed); ok { - return numeric >= 10 - } - if likelyDisplayLabel(trimmed) { - return false - } - return domainPrimitiveNamePattern.MatchString(trimmed) || strings.Contains(trimmed, "_") -} - -func duplicatedKnowledgeNumber(value string) (int, bool) { - number, err := strconv.Atoi(value) - if err != nil { - return 0, false - } - if number < 0 { - number = -number - } - return number, true -} - -func likelyDisplayLabel(value string) bool { - if strings.Contains(value, "_") { - return false - } - if strings.ContainsAny(value, "-/:.") { - return false - } - words := strings.Fields(value) - return len(words) > 0 && len(words) <= 3 -} - func unsafeScriptNumericConversion(text string) bool { lowered := strings.ToLower(text) return strings.Contains(lowered, "static_cast") || diff --git a/internal/codeguard/checks/quality/quality_precision_duplication.go b/internal/codeguard/checks/quality/quality_precision_duplication.go new file mode 100644 index 0000000..d701901 --- /dev/null +++ b/internal/codeguard/checks/quality/quality_precision_duplication.go @@ -0,0 +1,159 @@ +package quality + +import ( + "fmt" + "regexp" + "strconv" + "strings" + + "github.com/devr-tools/codeguard/internal/codeguard/checks/support" + "github.com/devr-tools/codeguard/internal/codeguard/core" +) + +func parsedDuplicatedKnowledgeFindings(env support.Context, file string, parsed *support.ParsedFile) []core.Finding { + if isQualityFixturePath(file) { + return nil + } + seen := map[string]int{} + for _, statement := range parsed.Module.Statements { + for _, literal := range domainKnowledgeLiterals(statement.Raw) { + if first, exists := seen[literal]; exists { + return []core.Finding{precisionWarnFinding(env, qualityDuplicatedKnowledgeRuleID, file, statement.Line, + fmt.Sprintf("business literal %s is duplicated near line %d; centralize shared domain knowledge", literal, first), core.ConfidenceLow)} + } + seen[literal] = statement.Line + } + } + return nil +} + +func sourceDuplicatedKnowledgeFindings(env support.Context, file string, source string) []core.Finding { + if isQualityFixturePath(file) { + return nil + } + seen := map[string]int{} + for idx, line := range strings.Split(strings.ReplaceAll(source, "\r\n", "\n"), "\n") { + if strings.TrimSpace(line) == "" { + continue + } + for _, literal := range domainKnowledgeLiterals(line) { + if first, exists := seen[literal]; exists { + return []core.Finding{precisionWarnFinding(env, qualityDuplicatedKnowledgeRuleID, file, idx+1, + fmt.Sprintf("business literal %s is duplicated near line %d; centralize shared domain knowledge", literal, first), core.ConfidenceLow)} + } + seen[literal] = idx + 1 + } + } + return nil +} + +func domainKnowledgeLiterals(line string) []string { + if duplicatedKnowledgeLineIsDisplayOnly(line) { + return nil + } + matches := regexp.MustCompile(`"([^"]{2,80})"|'([^']{2,80})'|\b\d+(?:\.\d+)?\b`).FindAllString(line, -1) + out := make([]string, 0, len(matches)) + for _, match := range matches { + if domainKnowledgeLiteralInLine(match, line) { + out = append(out, match) + } + } + return out +} + +func duplicatedKnowledgeLineIsDisplayOnly(line string) bool { + lowered := strings.ToLower(line) + if strings.Contains(lowered, "classname") || strings.Contains(lowered, "clasname") || strings.Contains(lowered, "class:") { + return true + } + if strings.Contains(line, "<") && strings.Contains(line, ">") { + return true + } + if strings.Contains(lowered, "label:") || strings.Contains(lowered, "placeholder:") || strings.Contains(lowered, "title:") || + strings.Contains(lowered, "aria-label") { + return true + } + return false +} + +func domainKnowledgeLiteral(value string) bool { + return domainKnowledgeLiteralInLine(value, "") +} + +func domainKnowledgeLiteralInLine(value string, line string) bool { + trimmed := strings.Trim(value, `"'`) + if trimmed == "" || len(trimmed) > 80 { + return false + } + if len(trimmed) < 4 && !strings.ContainsAny(trimmed, "0123456789") { + return false + } + if numeric, ok := duplicatedKnowledgeNumber(trimmed); ok { + return duplicatedKnowledgeNumericLiteral(numeric, line) + } + if duplicatedKnowledgeEnumStatusLiteral(trimmed, line) { + return false + } + if likelyDisplayLabel(trimmed) { + return false + } + return domainPrimitiveNamePattern.MatchString(trimmed) || strings.Contains(trimmed, "_") +} + +func duplicatedKnowledgeNumericLiteral(number int, line string) bool { + if number < 0 { + number = -number + } + if number < 100 { + return false + } + lowered := strings.ToLower(line) + return line == "" || + domainPrimitiveNamePattern.MatchString(lowered) || + durationNamePattern.MatchString(lowered) || + sizeNamePattern.MatchString(lowered) || + moneyNamePattern.MatchString(lowered) +} + +func duplicatedKnowledgeEnumStatusLiteral(value string, line string) bool { + if line == "" { + return false + } + trimmed := strings.TrimSpace(value) + if trimmed == "" { + return false + } + enumLike := strings.Contains(trimmed, "_") || strings.ToUpper(trimmed) == trimmed + if !enumLike { + return false + } + loweredLine := strings.ToLower(line) + for _, marker := range []string{"enum", "status", "type:", "kind:", "value:", "option", "label", "as const", "satisfies"} { + if strings.Contains(loweredLine, marker) { + return true + } + } + return false +} + +func duplicatedKnowledgeNumber(value string) (int, bool) { + number, err := strconv.Atoi(value) + if err != nil { + return 0, false + } + if number < 0 { + number = -number + } + return number, true +} + +func likelyDisplayLabel(value string) bool { + if strings.Contains(value, "_") { + return false + } + if strings.ContainsAny(value, "-/:.") { + return false + } + words := strings.Fields(value) + return len(words) > 0 && len(words) <= 3 +} diff --git a/internal/codeguard/checks/quality/quality_precision_ui_conventions.go b/internal/codeguard/checks/quality/quality_precision_ui_conventions.go index 23c5fd4..9551a3f 100644 --- a/internal/codeguard/checks/quality/quality_precision_ui_conventions.go +++ b/internal/codeguard/checks/quality/quality_precision_ui_conventions.go @@ -23,6 +23,23 @@ func isReactComponentOrHookBoundary(file string, fn precisionFunction) bool { return isTSXLikeSourcePath(file) && (strings.Contains(body, "jsx") || strings.Contains(body, "return <") || strings.Contains(body, "react.")) } +func isReactComponentOrNamedHookBoundary(file string, fn precisionFunction) bool { + if !isScriptLikeSourcePath(file) { + return false + } + if isReactHookName(fn.Name) { + return true + } + if isReactNativeComponentOrScreenBoundary(file, fn) { + return true + } + if isTSXLikeSourcePath(file) && isReactComponentName(fn.Name) { + return true + } + body := strings.ToLower(fn.Body) + return isTSXLikeSourcePath(file) && (strings.Contains(body, "jsx") || strings.Contains(body, "return <") || strings.Contains(body, "react.")) +} + func isTSXLikeSourcePath(file string) bool { lowered := strings.ToLower(file) return strings.HasSuffix(lowered, ".tsx") || strings.HasSuffix(lowered, ".jsx") @@ -135,6 +152,9 @@ func isAllowedBooleanUIName(file string, fn precisionFunction, name string) bool if isEventHandlerName(name) { return true } + if isConventionalNonPredicateName(name) { + return true + } if isResourceIdentifierName(name) { return true } @@ -149,6 +169,15 @@ func isAllowedBooleanUIName(file string, fn precisionFunction, name string) bool } } +func isConventionalNonPredicateName(name string) bool { + switch strings.ToLower(strings.Trim(name, "_$")) { + case "opts", "options", "message", "classname", "class", "icon", "submit", "compare", "parser", "parse", "renderer", "render": + return true + default: + return false + } +} + func isResourceIdentifierName(name string) bool { lowered := strings.ToLower(strings.Trim(name, "_$")) for _, suffix := range []string{"arn", "url", "uri", "id", "ids", "key", "token", "secret", "rolearn"} { @@ -160,18 +189,21 @@ func isResourceIdentifierName(name string) bool { } func conventionalCardinalityName(name string) bool { - switch strings.ToLower(strings.Trim(name, "_$")) { - case "args", "rows", "ids", "next", "out", "props", "searchparams", "params", "item", "items", "entries", "status", "k", "v", "i", "j", "x", "y": + base := strings.ToLower(strings.Trim(name, "_$")) + switch base { + case "answers", "args", "columns", "contracts", "entries", "ids", "items", "k", "matters", "next", "out", "params", "props", "risks", "rows", "searchparams", "sections", "status", "v", "i", "j", "x", "y": return true default: return len(name) <= 2 || - strings.HasSuffix(name, "ids") || - strings.HasSuffix(name, "rows") || - strings.HasSuffix(name, "items") || - strings.HasSuffix(name, "entries") || - strings.HasSuffix(name, "params") || - strings.HasSuffix(name, "props") || - strings.HasSuffix(name, "args") + strings.HasSuffix(base, "ids") || + strings.HasSuffix(base, "rows") || + strings.HasSuffix(base, "items") || + strings.HasSuffix(base, "entries") || + strings.HasSuffix(base, "params") || + strings.HasSuffix(base, "props") || + strings.HasSuffix(base, "args") || + strings.HasSuffix(base, "columns") || + strings.HasSuffix(base, "sections") } } diff --git a/internal/codeguard/checks/quality/quality_precision_workstreams_cd.go b/internal/codeguard/checks/quality/quality_precision_workstreams_cd.go index eb1f3ef..a09f941 100644 --- a/internal/codeguard/checks/quality/quality_precision_workstreams_cd.go +++ b/internal/codeguard/checks/quality/quality_precision_workstreams_cd.go @@ -30,7 +30,7 @@ const ( ) var ( - commandFunctionPrefixPattern = regexp.MustCompile(`^(add|append|assign|cancel|clear|close|create|delete|disable|emit|enable|insert|mutate|open|persist|publish|remove|reset|save|send|set|store|toggle|update|upsert|write)`) + commandFunctionPrefixPattern = regexp.MustCompile(`^(add|append|assign|cancel|clear|close|create|delete|disable|emit|enable|insert|mutate|notify|open|persist|publish|record|remove|reset|save|send|set|store|submit|toggle|update|upsert|upload|write)`) readCallPattern = regexp.MustCompile(`(?i)(^|[.>:\-_])(count|fetch|find|get|list|load|lookup|query|read|select|search)([A-Z_:\-.]|$)`) identifierTokenPattern = regexp.MustCompile(`[A-Za-z_$][A-Za-z0-9_$]*`) infraNamePattern = regexp.MustCompile(`(?i)(sql|http|redis|kafka|grpc|graphql|mongo|s3|dynamo|postgres|mysql|elastic|orm)`) @@ -176,6 +176,9 @@ func hiddenMutation(file string, fn precisionFunction) bool { if explicitMutationName(fn.Name) || isFrameworkOrchestrationBoundary(file, fn) || isScriptEntrypoint(file, fn.Name) { return false } + if isReactComponentOrNamedHookBoundary(file, fn) { + return false + } mutatesParam := mutatesParameter(fn) mutatesState := mutatingFunctionEvidence(fn) if isReactLocalStateBoundary(file, fn) && mutatesState && !mutatesParam && onlyReactHookLocalStateMutation(fn) { diff --git a/internal/codeguard/checks/quality/quality_smells.go b/internal/codeguard/checks/quality/quality_smells.go index cc93a9e..3867333 100644 --- a/internal/codeguard/checks/quality/quality_smells.go +++ b/internal/codeguard/checks/quality/quality_smells.go @@ -376,7 +376,7 @@ func responsibilityBucket(name string) string { func featureEnvyFindings(env support.Context, file string, functions []structuralFunction) []core.Finding { findings := make([]core.Finding, 0) for _, fn := range functions { - if isStructuralUIRenderingContext(file, fn) { + if isStructuralUIRenderingContext(file, fn) || isStructuralMapperOrBuilderContext(fn) { continue } if len(fn.Params) == 0 || fn.Body == "" { @@ -489,7 +489,7 @@ func messageChainFindings(env support.Context, file string, source string, langu if trimmed == "" || strings.HasPrefix(trimmed, "import ") || strings.HasPrefix(trimmed, "#include") || strings.HasPrefix(trimmed, "package ") { continue } - if chainSeparators(trimmed) >= 4 && !looksLikeAllowedFluentChain(trimmed) { + if chainSeparators(trimmed) >= 4 && !looksLikeAllowedFluentChain(trimmed) && !looksLikeAllowedTraversalChain(trimmed) { return []core.Finding{precisionWarnFinding(env, smellMessageChainRuleID, file, idx+1, "long message chain reaches through several collaborators; introduce a named query/helper at the boundary", core.ConfidenceMedium)} @@ -523,6 +523,23 @@ func looksLikeAllowedFluentChain(line string) bool { return strings.Contains(lowered, "builder") || strings.Contains(lowered, ".with") || strings.Contains(lowered, ".set") } +func looksLikeAllowedTraversalChain(line string) bool { + lowered := strings.ToLower(line) + if strings.Contains(line, "?.") { + return true + } + for _, marker := range []string{ + "response.", "result.", "payload.", "body.", "json.", "config.", "settings.", + "process.env", "import.meta.env", "params.", "query.", "headers.", + "row.", "record.", "dto.", "args.", + } { + if strings.Contains(lowered, marker) { + return true + } + } + return false +} + func dataClumpFindings(env support.Context, file string, functions []structuralFunction) []core.Finding { type occurrence struct { line int diff --git a/internal/codeguard/checks/quality/quality_smells_ui.go b/internal/codeguard/checks/quality/quality_smells_ui.go index 64d2774..a2a5c81 100644 --- a/internal/codeguard/checks/quality/quality_smells_ui.go +++ b/internal/codeguard/checks/quality/quality_smells_ui.go @@ -49,3 +49,41 @@ func isUIRenderMappingBody(body string) bool { strings.Contains(lowered, "route.params") || strings.Contains(lowered, "props.") && (strings.Contains(lowered, "theme") || strings.Contains(lowered, "style")) } + +func isStructuralMapperOrBuilderContext(fn structuralFunction) bool { + loweredName := strings.ToLower(strings.Trim(fn.Name, "_$")) + mapperName := false + for _, token := range []string{ + "build", "bucket", "collect", "derive", "format", "group", "map", "normalize", + "render", "rows", "serialize", "table", "to", "transform", "writeauditlog", + } { + if strings.Contains(loweredName, token) { + mapperName = true + break + } + } + if !mapperName { + return false + } + dominantName, dominantCount, totalExternal := dominantExternalAccess(fn) + if dominantCount < 5 || totalExternal < 5 { + return false + } + switch strings.ToLower(strings.Trim(dominantName, "_$")) { + case "args", "data", "dto", "input", "item", "message", "payload", "record", "response", "result", "row", "rows", "value": + return true + default: + return mapperReturnsConstructedValue(fn.Body) + } +} + +func mapperReturnsConstructedValue(body string) bool { + lowered := strings.ToLower(body) + return strings.Contains(lowered, "return {") || + strings.Contains(lowered, "return [") || + strings.Contains(lowered, "return new ") || + strings.Contains(lowered, "return object.assign") || + strings.Contains(lowered, "return array.from") || + strings.Contains(lowered, "return rows.map") || + strings.Contains(lowered, ".map(") +} diff --git a/tests/checks/design_local_abstraction_test.go b/tests/checks/design_local_abstraction_test.go index 65e2cbd..258c0ef 100644 --- a/tests/checks/design_local_abstraction_test.go +++ b/tests/checks/design_local_abstraction_test.go @@ -287,6 +287,35 @@ func TestDesignPersistenceModelLeakSkipsNestJSDTOControllerBoundaries(t *testing } } +func TestDesignPersistenceModelLeakAllowsPackageAPIImplementationBoundary(t *testing.T) { + dir := t.TempDir() + writeFile(t, filepath.Join(dir, "packages", "api", "src", "claims", "repository.ts"), strings.Join([]string{ + "import { PrismaClient } from '@prisma/client';", + "export async function loadClaimRows(db: PrismaClient) {", + " // Convert database row shapes before returning API DTOs.", + " const rows = await db.claim.findMany();", + " return rows.map((row) => ({ id: row.id }));", + "}", + }, "\n")) + writeFile(t, filepath.Join(dir, "packages", "domain", "claims", "model.ts"), strings.Join([]string{ + "export type ClaimModel = { id: string };", + }, "\n")) + + report := runDesignLocalScan(t, designLocalConfig(dir, "typescript")) + + assertFindingRulePresent(t, report, "Design Patterns", "design.persistence-model-leak") + for _, section := range report.Sections { + if section.Name != "Design Patterns" { + continue + } + for _, finding := range section.Findings { + if finding.RuleID == "design.persistence-model-leak" && strings.Contains(finding.Path, "packages/api/src") { + t.Fatalf("packages/api/src is an implementation boundary and should allow persistence vocabulary: %+v", finding) + } + } + } +} + func TestDesignPersistenceModelLeakKeepsAPIAndDomainBoundaries(t *testing.T) { dir := t.TempDir() writeFile(t, filepath.Join(dir, "apps", "web", "app", "api", "contracts", "route.ts"), strings.Join([]string{ diff --git a/tests/checks/function_hidden_mutation_noise_test.go b/tests/checks/function_hidden_mutation_noise_test.go index 751d11a..8bdd4c9 100644 --- a/tests/checks/function_hidden_mutation_noise_test.go +++ b/tests/checks/function_hidden_mutation_noise_test.go @@ -274,6 +274,53 @@ func TestFunctionHiddenMutationAllowsReactNativeLocalStateAndHandlers(t *testing assertFindingRuleAbsent(t, report, "Code Quality", "function.hidden-mutation") } +func TestFunctionHiddenMutationAllowsReactComponentsAndHooksAsBoundaries(t *testing.T) { + cases := []struct { + name string + language string + file string + source []string + }{ + { + name: "uppercase react component", + language: "typescript", + file: "apps/web/app/claims/_components/ClaimEditDialog.tsx", + source: []string{ + "export function ClaimEditDialog(repo: Repository, claim: Claim) {", + " repo.save(claim);", + " return ;", + "}", + "interface Repository { save(input: Claim): void }", + "interface Claim { id: string }", + }, + }, + { + name: "react hook orchestrator", + language: "typescript", + file: "apps/web/app/files/useNewVersionUpload.ts", + source: []string{ + "export function useNewVersionUpload(uploader: Uploader, file: File) {", + " uploader.upload(file);", + " return { uploading: true };", + "}", + "interface Uploader { upload(input: File): void }", + "interface File { id: string }", + }, + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + dir := t.TempDir() + writeFile(t, filepath.Join(dir, tc.file), strings.Join(tc.source, "\n")) + + report := runQualityPrecisionScan(t, qualityPrecisionConfigForLanguage(dir, tc.language)) + + assertFindingRuleAbsent(t, report, "Code Quality", "function.hidden-mutation") + assertFindingRuleAbsent(t, report, "Code Quality", "quality.hidden-side-effect") + }) + } +} + func TestFunctionHiddenMutationStillWarnsForReactNativeCollaboratorMutation(t *testing.T) { dir := t.TempDir() writeFile(t, filepath.Join(dir, "apps/mobile/src/screens/ProfileScreen.tsx"), strings.Join([]string{ diff --git a/tests/checks/function_precision_test.go b/tests/checks/function_precision_test.go index 7dc2711..749b02e 100644 --- a/tests/checks/function_precision_test.go +++ b/tests/checks/function_precision_test.go @@ -77,6 +77,34 @@ func TestFunctionCommandQueryMixWarnsWhenQueryMutatesState(t *testing.T) { assertFindingLevel(t, report, "Code Quality", "function.command-query-mix", "warn") } +func TestFunctionCommandQueryMixAllowsCommandsReturningUsefulResults(t *testing.T) { + cases := []string{ + "cancelTextEdit", + "createNewFile", + "notify", + "recordJobRun", + "uploadVersion", + } + for _, name := range cases { + t.Run(name, func(t *testing.T) { + dir := t.TempDir() + writeFile(t, filepath.Join(dir, "command.ts"), strings.Join([]string{ + "export async function " + name + "(repo: Repository, input: Input) {", + " await repo.save(input);", + " return repo.find(input.id);", + "}", + "interface Repository { save(input: Input): Promise; find(id: string): Promise }", + "interface Input { id: string }", + }, "\n")) + + report := runQualityPrecisionScan(t, qualityPrecisionConfigForLanguage(dir, "typescript")) + + assertFindingRuleAbsent(t, report, "Code Quality", "function.command-query-mix") + assertFindingRuleAbsent(t, report, "Code Quality", "quality.hidden-side-effect") + }) + } +} + func TestFunctionHiddenMutationWarnsAcrossLanguages(t *testing.T) { cases := []struct { name string diff --git a/tests/checks/quality_ui_false_positive_hardening_test.go b/tests/checks/quality_ui_false_positive_hardening_test.go index 91c5f00..4642b00 100644 --- a/tests/checks/quality_ui_false_positive_hardening_test.go +++ b/tests/checks/quality_ui_false_positive_hardening_test.go @@ -170,14 +170,14 @@ func TestQualityDuplicatedKnowledgeSkipsDisplayStringsAndIncludesLiteral(t *test "export function Labels() {", " return
Status
;", "}", - "export const first = 'claim_status_code';", - "export const second = 'claim_status_code';", + "export const first = 'invoice_policy_code';", + "export const second = 'invoice_policy_code';", }, "\n")) report := runQualityPrecisionScan(t, qualityPrecisionConfigForLanguage(dir, "typescript")) finding := firstFindingForRule(t, report, "Code Quality", "quality.duplicated-knowledge") - if !strings.Contains(finding.Message, "'claim_status_code'") { + if !strings.Contains(finding.Message, "'invoice_policy_code'") { t.Fatalf("expected duplicated literal in message, got %q", finding.Message) } } @@ -189,8 +189,8 @@ func TestQualityDuplicatedKnowledgeSkipsTrivialRepeatedNumbers(t *testing.T) { "export const start = 0;", "export const second = 2;", "export const columns = 2;", - "export const statusA = 'claim_status_code';", - "export const statusB = 'claim_status_code';", + "export const policyA = 'invoice_policy_code';", + "export const policyB = 'invoice_policy_code';", }, "\n")) report := runQualityPrecisionScan(t, qualityPrecisionConfigForLanguage(dir, "typescript")) @@ -199,11 +199,37 @@ func TestQualityDuplicatedKnowledgeSkipsTrivialRepeatedNumbers(t *testing.T) { if strings.Contains(finding.Message, " 0 ") || strings.Contains(finding.Message, " 2 ") { t.Fatalf("expected duplicated domain literal instead of trivial number, got %q", finding.Message) } - if !strings.Contains(finding.Message, "'claim_status_code'") { + if !strings.Contains(finding.Message, "'invoice_policy_code'") { t.Fatalf("expected duplicated domain literal in message, got %q", finding.Message) } } +func TestQualityDuplicatedKnowledgeSkipsSmallNumbersAndEnumStatusStrings(t *testing.T) { + dir := t.TempDir() + writeFile(t, filepath.Join(dir, "apps/web/app/claims/statuses.ts"), strings.Join([]string{ + "export const retryAttempts = 3;", + "export const visibleColumns = 3;", + "export const pageSize = 25;", + "export const defaultLimit = 25;", + "export const statuses = [", + " { value: 'CLAIM_APPROVED', label: 'Approved' },", + " { value: 'CLAIM_APPROVED', label: 'Approved' },", + "];", + "export const premiumAmountCents = 1000;", + "export const vipAmountCents = 1000;", + }, "\n")) + + report := runQualityPrecisionScan(t, qualityPrecisionConfigForLanguage(dir, "typescript")) + + finding := firstFindingForRule(t, report, "Code Quality", "quality.duplicated-knowledge") + if strings.Contains(finding.Message, "CLAIM_APPROVED") || strings.Contains(finding.Message, "25") || strings.Contains(finding.Message, "3") { + t.Fatalf("expected strong numeric domain duplicate, got %q", finding.Message) + } + if !strings.Contains(finding.Message, "1000") { + t.Fatalf("expected duplicated money-like numeric literal, got %q", finding.Message) + } +} + func TestNamingCardinalityMismatchAllowsFrameworkConventions(t *testing.T) { dir := t.TempDir() writeFile(t, filepath.Join(dir, "apps/web/app/okrs/use-kr-drag.ts"), strings.Join([]string{ @@ -238,6 +264,25 @@ func TestNamingCardinalityMismatchAllowsCollectionSuffixesAndMapPairs(t *testing assertFindingRuleAbsent(t, report, "Code Quality", "naming.cardinality-mismatch") } +func TestNamingCardinalityMismatchAllowsCommonPluralDomainCollections(t *testing.T) { + dir := t.TempDir() + writeFile(t, filepath.Join(dir, "apps/web/app/matters/list.ts"), strings.Join([]string{ + "export function renderSections(answers: Answer[], contracts: Contract[], matters: Matter[], risks: Risk[], sections: Section[], columns: Column[]) {", + " return { answers, contracts, matters, risks, sections, columns };", + "}", + "interface Answer { id: string }", + "interface Contract { id: string }", + "interface Matter { id: string }", + "interface Risk { id: string }", + "interface Section { id: string }", + "interface Column { id: string }", + }, "\n")) + + report := runQualityPrecisionScan(t, qualityPrecisionConfigForLanguage(dir, "typescript")) + + assertFindingRuleAbsent(t, report, "Code Quality", "naming.cardinality-mismatch") +} + func TestQualityMutableGlobalStateIgnoresReactLocalBindings(t *testing.T) { dir := t.TempDir() writeFile(t, filepath.Join(dir, "apps/web/app/claims/claim-classification-fields.tsx"), strings.Join([]string{ @@ -284,6 +329,19 @@ func TestNamingBooleanNotPredicateAllowsHandlersAndResourceIdentifiers(t *testin assertFindingRuleAbsent(t, report, "Code Quality", "naming.boolean-not-predicate") } +func TestNamingBooleanNotPredicateAllowsConventionalNonBooleanNames(t *testing.T) { + dir := t.TempDir() + writeFile(t, filepath.Join(dir, "apps/web/app/components/actions.tsx"), strings.Join([]string{ + "export function ActionButton(opts: boolean, message: boolean, className: boolean, Icon: boolean, submit: boolean, compare: boolean, parser: boolean) {", + " return ;", + "}", + }, "\n")) + + report := runQualityPrecisionScan(t, qualityPrecisionConfigForLanguage(dir, "typescript")) + + assertFindingRuleAbsent(t, report, "Code Quality", "naming.boolean-not-predicate") +} + func TestFunctionCommandQueryMixAllowsLocalBuilderMutation(t *testing.T) { dir := t.TempDir() writeFile(t, filepath.Join(dir, "apps/web/lib/filters.ts"), strings.Join([]string{ @@ -408,6 +466,35 @@ func TestUISmellAndOverflowRulesSkipReactNativeRenderingHelpers(t *testing.T) { } } +func TestStructuralSmellsSkipAPIConfigTraversalAndDTOBuilders(t *testing.T) { + dir := t.TempDir() + writeFile(t, filepath.Join(dir, "packages/api/src/contracts/mappers.ts"), strings.Join([]string{ + "export function buildContractDto(row: Row) {", + " return {", + " id: row.contract.version.current.owner.profile.id,", + " ownerName: row.contract.version.current.owner.profile.name,", + " ownerEmail: row.contract.version.current.owner.profile.email,", + " status: row.contract.version.current.status.code,", + " updatedAt: row.contract.version.current.timestamps.updatedAt,", + " };", + "}", + "export function readApiResponse(response: ApiResponse) {", + " return response.data?.claim?.owner?.profile?.department?.name;", + "}", + "export function readConfig(config: AppConfig) {", + " return config.services.api.endpoints.claims.primary.url;", + "}", + "interface Row { contract: any }", + "interface ApiResponse { data?: any }", + "interface AppConfig { services: any }", + }, "\n")) + + report := runQualityPrecisionScan(t, qualityPrecisionConfigForLanguage(dir, "typescript")) + + assertFindingRuleAbsent(t, report, "Code Quality", "smell.feature-envy") + assertFindingRuleAbsent(t, report, "Code Quality", "smell.message-chain") +} + func TestSmellAndOverflowRulesStillFlagNonUIProductionCode(t *testing.T) { dir := t.TempDir() writeFile(t, filepath.Join(dir, "packages/domain/src/account-risk.ts"), strings.Join([]string{