@@ -6,63 +6,99 @@ import { lintConfig } from '../src/commands/lint';
66const RULE = 'naming/namespace-prefix' ;
77const prefixIssues = ( config : any ) => lintConfig ( config ) . filter ( ( i ) => i . rule === RULE ) ;
88
9- describe ( 'lint — namespace-prefix advisory (ADR-0048 §3.3)' , ( ) => {
10- it ( 'warns on a bare-named page without the namespace prefix' , ( ) => {
9+ describe ( 'lint — intra-package duplicate-name advisory (ADR-0048 §3.4)' , ( ) => {
10+ it ( 'stays silent on unique bare names — they are NOT a collision (ADR-0048 §3.4)' , ( ) => {
11+ // The cross-package throw was retired: distinct packages coexist on the
12+ // same bare name via package-scoped resolution. A single package with
13+ // unique bare names must produce zero warnings (was 63 false positives
14+ // for hotcrm under the old over-broad rule).
1115 const issues = prefixIssues ( {
1216 manifest : { namespace : 'crm' } ,
13- pages : [ { name : 'home' , label : 'Home' } ] ,
17+ apps : [ { name : 'crm' } ] ,
18+ pages : [ { name : 'home' } , { name : 'settings' } ] ,
19+ dashboards : [ { name : 'overview' } ] ,
20+ flows : [ { name : 'onboard' } ] ,
21+ actions : [ { name : 'send' } ] ,
22+ reports : [ { name : 'pipeline' } ] ,
23+ datasets : [ { name : 'sales' } ] ,
24+ } ) ;
25+ expect ( issues ) . toHaveLength ( 0 ) ;
26+ } ) ;
27+
28+ it ( 'warns on an actual intra-package duplicate (type, name) pair' , ( ) => {
29+ const issues = prefixIssues ( {
30+ manifest : { namespace : 'crm' } ,
31+ pages : [ { name : 'home' , label : 'Home' } , { name : 'home' , label : 'Home 2' } ] ,
1432 } ) ;
1533 expect ( issues ) . toHaveLength ( 1 ) ;
1634 expect ( issues [ 0 ] . severity ) . toBe ( 'warning' ) ;
17- expect ( issues [ 0 ] . path ) . toBe ( 'pages[0].name' ) ;
18- expect ( issues [ 0 ] . fix ) . toBe ( 'crm_home' ) ;
35+ // The warning points at the duplicate occurrence, not the first.
36+ expect ( issues [ 0 ] . path ) . toBe ( 'pages[1].name' ) ;
37+ expect ( issues [ 0 ] . message ) . toContain ( 'declared more than once' ) ;
38+ expect ( issues [ 0 ] . message ) . toContain ( 'pages[0].name' ) ;
1939 expect ( issues [ 0 ] . message ) . toContain ( 'ADR-0048' ) ;
40+ // Suggests a namespace-prefixed rename when a namespace is available.
41+ expect ( issues [ 0 ] . fix ) . toBe ( 'crm_home' ) ;
2042 } ) ;
2143
22- it ( 'accepts a namespace-prefixed name' , ( ) => {
23- expect (
24- prefixIssues ( { manifest : { namespace : 'crm' } , flows : [ { name : 'crm_onboarding' } ] } ) ,
25- ) . toHaveLength ( 0 ) ;
44+ it ( 'does not claim the package will fail at install' , ( ) => {
45+ const issues = prefixIssues ( {
46+ manifest : { namespace : 'crm' } ,
47+ flows : [ { name : 'onboard' } , { name : 'onboard' } ] ,
48+ } ) ;
49+ expect ( issues ) . toHaveLength ( 1 ) ;
50+ // ADR-0048 §3.4 retired the per-item cross-package throw — the old
51+ // "collide on the registry key and fail at install" claim is false.
52+ expect ( issues [ 0 ] . message ) . not . toContain ( 'fail at install' ) ;
53+ expect ( issues [ 0 ] . message ) . not . toContain ( 'Two packages' ) ;
2654 } ) ;
2755
28- it ( 'exempts an app named after the namespace (ADR-0019 single-app convention)' , ( ) => {
29- // `defineApp({ name: 'crm' })` in namespace `crm` must NOT warn.
30- expect (
31- prefixIssues ( { manifest : { namespace : 'crm' } , apps : [ { name : 'crm' } ] } ) ,
32- ) . toHaveLength ( 0 ) ;
56+ it ( 'detects duplicates per type independently across every bare-named type' , ( ) => {
57+ const issues = prefixIssues ( {
58+ manifest : { namespace : 'crm' } ,
59+ apps : [ { name : 'a' } , { name : 'a' } ] ,
60+ pages : [ { name : 'p' } , { name : 'p' } ] ,
61+ dashboards : [ { name : 'd' } , { name : 'd' } ] ,
62+ flows : [ { name : 'f' } , { name : 'f' } ] ,
63+ actions : [ { name : 'ac' } , { name : 'ac' } ] ,
64+ reports : [ { name : 'r' } , { name : 'r' } ] ,
65+ datasets : [ { name : 'ds' } , { name : 'ds' } ] ,
66+ } ) ;
67+ expect ( issues ) . toHaveLength ( 7 ) ;
68+ expect ( new Set ( issues . map ( ( i ) => i . severity ) ) ) . toEqual ( new Set ( [ 'warning' ] ) ) ;
3369 } ) ;
3470
35- it ( 'exempts platform-reserved sys_ names' , ( ) => {
71+ it ( 'treats the same name under different types as distinct (no false positive)' , ( ) => {
72+ // `page/home` and `flow/home` live under different registry collections —
73+ // they do not collide, so a shared name across types must not warn.
3674 expect (
37- prefixIssues ( { manifest : { namespace : 'crm' } , pages : [ { name : 'sys_admin' } ] } ) ,
75+ prefixIssues ( {
76+ manifest : { namespace : 'crm' } ,
77+ pages : [ { name : 'home' } ] ,
78+ flows : [ { name : 'home' } ] ,
79+ } ) ,
3880 ) . toHaveLength ( 0 ) ;
3981 } ) ;
4082
41- it ( 'covers every bare-named UI/automation type' , ( ) => {
83+ it ( 'warns on duplicates even when no namespace is declared (omits the fix)' , ( ) => {
84+ // Duplicate names shadow each other regardless of namespace; without a
85+ // namespace there is no prefix to suggest, so no `fix` is offered.
4286 const issues = prefixIssues ( {
43- manifest : { namespace : 'crm' } ,
44- apps : [ { name : 'other' } ] ,
45- pages : [ { name : 'home' } ] ,
46- dashboards : [ { name : 'overview' } ] ,
47- flows : [ { name : 'onboard' } ] ,
48- actions : [ { name : 'send' } ] ,
49- reports : [ { name : 'pipeline' } ] ,
50- datasets : [ { name : 'sales' } ] ,
87+ pages : [ { name : 'home' } , { name : 'home' } ] ,
5188 } ) ;
52- expect ( issues ) . toHaveLength ( 7 ) ;
53- expect ( new Set ( issues . map ( ( i ) => i . severity ) ) ) . toEqual ( new Set ( [ 'warning' ] ) ) ;
54- } ) ;
55-
56- it ( 'is silent when the package declares no namespace' , ( ) => {
57- // No manifest.namespace → nothing to prefix against; stays quiet.
58- expect ( prefixIssues ( { pages : [ { name : 'home' } ] } ) ) . toHaveLength ( 0 ) ;
89+ expect ( issues ) . toHaveLength ( 1 ) ;
90+ expect ( issues [ 0 ] . fix ) . toBeUndefined ( ) ;
91+ expect ( issues [ 0 ] . message ) . toContain ( 'declared more than once' ) ;
5992 } ) ;
6093
6194 it ( 'does not touch objects (already prefix-enforced as an error) or views' , ( ) => {
6295 const issues = prefixIssues ( {
6396 manifest : { namespace : 'crm' } ,
64- objects : [ { name : 'lead' , fields : { id : { type : 'text' } } } ] ,
65- views : [ { name : 'lead.all' } ] ,
97+ objects : [
98+ { name : 'lead' , fields : { id : { type : 'text' } } } ,
99+ { name : 'lead' , fields : { id : { type : 'text' } } } ,
100+ ] ,
101+ views : [ { name : 'lead.all' } , { name : 'lead.all' } ] ,
66102 } ) ;
67103 expect ( issues ) . toHaveLength ( 0 ) ;
68104 } ) ;
0 commit comments