@@ -2,7 +2,13 @@ import { describe, expect, it } from "vitest";
22
33import type { EnvironmentsConfig } from "../../src/index.js" ;
44import type { TestResult } from "../../src/model.js" ;
5- import { matchEnvironment } from "../../src/utils/environment.js" ;
5+ import {
6+ MAX_ENVIRONMENT_NAME_LENGTH ,
7+ assertValidEnvironmentName ,
8+ formatNormalizedEnvironmentCollision ,
9+ matchEnvironment ,
10+ validateEnvironmentName ,
11+ } from "../../src/utils/environment.js" ;
612
713const fixtures = {
814 envConfig : {
@@ -42,3 +48,99 @@ describe("matchEnvironment", () => {
4248 expect ( result ) . toEqual ( "default" ) ;
4349 } ) ;
4450} ) ;
51+
52+ describe ( "validateEnvironmentName" , ( ) => {
53+ it ( "accepts valid names and returns normalized value" , ( ) => {
54+ const validBoundaryName = "a" . repeat ( MAX_ENVIRONMENT_NAME_LENGTH ) ;
55+
56+ expect ( validateEnvironmentName ( "foo" ) ) . toEqual ( { valid : true , normalized : "foo" } ) ;
57+ expect ( validateEnvironmentName ( "__proto__" ) ) . toEqual ( { valid : true , normalized : "__proto__" } ) ;
58+ expect ( validateEnvironmentName ( "прод" ) ) . toEqual ( { valid : true , normalized : "прод" } ) ;
59+ expect ( validateEnvironmentName ( validBoundaryName ) ) . toEqual ( { valid : true , normalized : validBoundaryName } ) ;
60+ expect ( validateEnvironmentName ( " foo " ) ) . toEqual ( { valid : true , normalized : "foo" } ) ;
61+ expect ( validateEnvironmentName ( " default " ) ) . toEqual ( { valid : true , normalized : "default" } ) ;
62+ } ) ;
63+
64+ it ( "accepts names previously blocked by filesystem-style checks" , ( ) => {
65+ expect ( validateEnvironmentName ( "foo/bar" ) ) . toEqual ( { valid : true , normalized : "foo/bar" } ) ;
66+ expect ( validateEnvironmentName ( "foo#bar" ) ) . toEqual ( { valid : true , normalized : "foo#bar" } ) ;
67+ expect ( validateEnvironmentName ( "foo%bar" ) ) . toEqual ( { valid : true , normalized : "foo%bar" } ) ;
68+ expect ( validateEnvironmentName ( "foo:bar" ) ) . toEqual ( { valid : true , normalized : "foo:bar" } ) ;
69+ expect ( validateEnvironmentName ( "." ) ) . toEqual ( { valid : true , normalized : "." } ) ;
70+ expect ( validateEnvironmentName ( ".." ) ) . toEqual ( { valid : true , normalized : ".." } ) ;
71+ } ) ;
72+
73+ it ( "rejects empty and whitespace-only names" , ( ) => {
74+ expect ( validateEnvironmentName ( "" ) ) . toEqual ( {
75+ valid : false ,
76+ reason : "name must not be empty" ,
77+ } ) ;
78+ expect ( validateEnvironmentName ( " " ) ) . toEqual ( {
79+ valid : false ,
80+ reason : "name must not be empty" ,
81+ } ) ;
82+ } ) ;
83+
84+ it ( "rejects too long names after trim" , ( ) => {
85+ const tooLongName = ` ${ "a" . repeat ( MAX_ENVIRONMENT_NAME_LENGTH + 1 ) } ` ;
86+
87+ expect ( validateEnvironmentName ( tooLongName ) ) . toEqual ( {
88+ valid : false ,
89+ reason : `name must not exceed ${ MAX_ENVIRONMENT_NAME_LENGTH } characters` ,
90+ } ) ;
91+ } ) ;
92+
93+ it ( "rejects control characters" , ( ) => {
94+ expect ( validateEnvironmentName ( "foo\nbar" ) ) . toEqual ( {
95+ valid : false ,
96+ reason : "name must not contain control characters" ,
97+ } ) ;
98+ expect ( validateEnvironmentName ( "foo\tbar" ) ) . toEqual ( {
99+ valid : false ,
100+ reason : "name must not contain control characters" ,
101+ } ) ;
102+ expect ( validateEnvironmentName ( "foo\u0000bar" ) ) . toEqual ( {
103+ valid : false ,
104+ reason : "name must not contain control characters" ,
105+ } ) ;
106+ expect ( validateEnvironmentName ( "foo\u009Fbar" ) ) . toEqual ( {
107+ valid : false ,
108+ reason : "name must not contain control characters" ,
109+ } ) ;
110+ expect ( validateEnvironmentName ( "foo\rbar" ) ) . toEqual ( {
111+ valid : false ,
112+ reason : "name must not contain control characters" ,
113+ } ) ;
114+ expect ( validateEnvironmentName ( "foo\r\nbar" ) ) . toEqual ( {
115+ valid : false ,
116+ reason : "name must not contain control characters" ,
117+ } ) ;
118+ } ) ;
119+
120+ it ( "rejects non-string input" , ( ) => {
121+ expect ( validateEnvironmentName ( 1 ) ) . toEqual ( {
122+ valid : false ,
123+ reason : "name must be a string" ,
124+ } ) ;
125+ } ) ;
126+ } ) ;
127+
128+ describe ( "assertValidEnvironmentName" , ( ) => {
129+ it ( "returns normalized value" , ( ) => {
130+ expect ( assertValidEnvironmentName ( " foo " ) ) . toBe ( "foo" ) ;
131+ } ) ;
132+
133+ it ( "throws with source details" , ( ) => {
134+ expect ( ( ) => assertValidEnvironmentName ( "" , "config.environment" ) ) . toThrow (
135+ 'Invalid config.environment "": name must not be empty' ,
136+ ) ;
137+ } ) ;
138+ } ) ;
139+
140+ describe ( "formatNormalizedEnvironmentCollision" , ( ) => {
141+ it ( "formats stable collision message" , ( ) => {
142+ expect ( formatNormalizedEnvironmentCollision ( "config.environments" , "foo" , [ "foo" , " foo " ] ) ) . toBe (
143+ 'config.environments: normalized key "foo" is produced by original keys ["foo"," foo "]' ,
144+ ) ;
145+ } ) ;
146+ } ) ;
0 commit comments