11import { MapboxApiBasedTool } from '../MapboxApiBasedTool.js' ;
2- import { ListTokensTool } from '../list-tokens-tool/ListTokensTool.js' ;
32import { StyleComparisonTool } from './StyleComparisonTool.js' ;
43
54describe ( 'StyleComparisonTool' , ( ) => {
65 let tool : StyleComparisonTool ;
7- let mockListTokensTool : jest . SpyInstance ;
86
97 beforeEach ( ( ) => {
108 tool = new StyleComparisonTool ( ) ;
@@ -33,37 +31,19 @@ describe('StyleComparisonTool', () => {
3331 expect ( url ) . toContain ( 'after=mapbox%2Foutdoors-v12' ) ;
3432 } ) ;
3533
36- it ( 'should attempt to fetch public token when no token provided' , async ( ) => {
37- mockListTokensTool = jest
38- . spyOn ( ListTokensTool . prototype , 'run' )
39- . mockResolvedValue ( {
40- isError : false ,
41- content : [
42- {
43- type : 'text' ,
44- text : JSON . stringify ( {
45- tokens : [
46- {
47- token : 'pk.fetched.token' ,
48- name : 'Public Token'
49- }
50- ]
51- } )
52- }
53- ]
54- } ) ;
55-
34+ it ( 'should require access token' , async ( ) => {
5635 const input = {
5736 before : 'mapbox/streets-v11' ,
5837 after : 'mapbox/satellite-v9'
38+ // Missing accessToken
5939 } ;
6040
61- const result = await tool . run ( input ) ;
41+ const result = await tool . run ( input as any ) ;
6242
63- expect ( result . isError ) . toBe ( false ) ;
64- expect ( mockListTokensTool ) . toHaveBeenCalledWith ( { usage : 'pk' } ) ;
65- const url = ( result . content [ 0 ] as { type : 'text' ; text : string } ) . text ;
66- expect ( url ) . toContain ( 'access_token=pk.fetched.token ' ) ;
43+ expect ( result . isError ) . toBe ( true ) ;
44+ expect (
45+ ( result . content [ 0 ] as { type : 'text' ; text : string } ) . text
46+ ) . toContain ( 'Required ' ) ;
6747 } ) ;
6848
6949 it ( 'should handle full style URLs' , async ( ) => {
@@ -101,54 +81,7 @@ describe('StyleComparisonTool', () => {
10181 expect ( url ) . toContain ( 'after=testuser%2Fstyle-id-2' ) ;
10282 } ) ;
10383
104- it ( 'should reject secret tokens and try to fetch public token' , async ( ) => {
105- mockListTokensTool = jest
106- . spyOn ( ListTokensTool . prototype , 'run' )
107- . mockResolvedValue ( {
108- isError : false ,
109- content : [
110- {
111- type : 'text' ,
112- text : JSON . stringify ( {
113- tokens : [
114- {
115- token : 'pk.fetched.public.token' ,
116- name : 'Public Token'
117- }
118- ]
119- } )
120- }
121- ]
122- } ) ;
123-
124- const input = {
125- before : 'mapbox/streets-v11' ,
126- after : 'mapbox/outdoors-v12' ,
127- accessToken : 'sk.secret.token'
128- } ;
129-
130- const result = await tool . run ( input ) ;
131-
132- expect ( result . isError ) . toBe ( false ) ;
133- expect ( mockListTokensTool ) . toHaveBeenCalledWith ( { usage : 'pk' } ) ;
134- const url = ( result . content [ 0 ] as { type : 'text' ; text : string } ) . text ;
135- expect ( url ) . toContain ( 'access_token=pk.fetched.public.token' ) ;
136- expect ( url ) . not . toContain ( 'sk.secret.token' ) ;
137- } ) ;
138-
139- it ( 'should error when secret token provided and no public token available' , async ( ) => {
140- mockListTokensTool = jest
141- . spyOn ( ListTokensTool . prototype , 'run' )
142- . mockResolvedValue ( {
143- isError : false ,
144- content : [
145- {
146- type : 'text' ,
147- text : JSON . stringify ( { tokens : [ ] } )
148- }
149- ]
150- } ) ;
151-
84+ it ( 'should reject secret tokens' , async ( ) => {
15285 const input = {
15386 before : 'mapbox/streets-v11' ,
15487 after : 'mapbox/outdoors-v12' ,
@@ -160,33 +93,25 @@ describe('StyleComparisonTool', () => {
16093 expect ( result . isError ) . toBe ( true ) ;
16194 expect (
16295 ( result . content [ 0 ] as { type : 'text' ; text : string } ) . text
163- ) . toContain ( 'Secret tokens (sk.*) cannot be used for style comparison' ) ;
96+ ) . toContain ( 'Invalid token type' ) ;
97+ expect (
98+ ( result . content [ 0 ] as { type : 'text' ; text : string } ) . text
99+ ) . toContain ( 'Secret tokens (sk.*) cannot be exposed' ) ;
164100 } ) ;
165101
166- it ( 'should return error when no token available' , async ( ) => {
167- mockListTokensTool = jest
168- . spyOn ( ListTokensTool . prototype , 'run' )
169- . mockResolvedValue ( {
170- isError : false ,
171- content : [
172- {
173- type : 'text' ,
174- text : JSON . stringify ( { tokens : [ ] } )
175- }
176- ]
177- } ) ;
178-
102+ it ( 'should reject invalid token formats' , async ( ) => {
179103 const input = {
180104 before : 'mapbox/streets-v11' ,
181- after : 'mapbox/outdoors-v12'
105+ after : 'mapbox/outdoors-v12' ,
106+ accessToken : 'invalid.token'
182107 } ;
183108
184109 const result = await tool . run ( input ) ;
185110
186111 expect ( result . isError ) . toBe ( true ) ;
187112 expect (
188113 ( result . content [ 0 ] as { type : 'text' ; text : string } ) . text
189- ) . toContain ( 'No access token provided ' ) ;
114+ ) . toContain ( 'Invalid token type ' ) ;
190115 } ) ;
191116
192117 it ( 'should return error for style ID without valid username in token' , async ( ) => {
@@ -228,6 +153,113 @@ describe('StyleComparisonTool', () => {
228153 expect ( url ) . toContain ( 'before=user-name%2Fstyle-id-1' ) ;
229154 expect ( url ) . toContain ( 'after=user-name%2Fstyle-id-2' ) ;
230155 } ) ;
156+
157+ it ( 'should include nocache parameter when noCache is true' , async ( ) => {
158+ const input = {
159+ before : 'mapbox/streets-v11' ,
160+ after : 'mapbox/outdoors-v12' ,
161+ accessToken : 'pk.test.token' ,
162+ noCache : true
163+ } ;
164+
165+ const result = await tool . run ( input ) ;
166+
167+ expect ( result . isError ) . toBe ( false ) ;
168+ const url = ( result . content [ 0 ] as { type : 'text' ; text : string } ) . text ;
169+ expect ( url ) . toContain ( 'nocache=true' ) ;
170+ } ) ;
171+
172+ it ( 'should not include nocache parameter when noCache is false or undefined' , async ( ) => {
173+ const input = {
174+ before : 'mapbox/streets-v11' ,
175+ after : 'mapbox/outdoors-v12' ,
176+ accessToken : 'pk.test.token' ,
177+ noCache : false
178+ } ;
179+
180+ const result = await tool . run ( input ) ;
181+
182+ expect ( result . isError ) . toBe ( false ) ;
183+ const url = ( result . content [ 0 ] as { type : 'text' ; text : string } ) . text ;
184+ expect ( url ) . not . toContain ( 'nocache' ) ;
185+
186+ // Test with undefined (default)
187+ const inputWithoutNoCache = {
188+ before : 'mapbox/streets-v11' ,
189+ after : 'mapbox/outdoors-v12' ,
190+ accessToken : 'pk.test.token'
191+ } ;
192+
193+ const result2 = await tool . run ( inputWithoutNoCache ) ;
194+ expect ( result2 . isError ) . toBe ( false ) ;
195+ const url2 = ( result2 . content [ 0 ] as { type : 'text' ; text : string } ) . text ;
196+ expect ( url2 ) . not . toContain ( 'nocache' ) ;
197+ } ) ;
198+
199+ it ( 'should include hash fragment with map position when coordinates are provided' , async ( ) => {
200+ const input = {
201+ before : 'mapbox/streets-v11' ,
202+ after : 'mapbox/outdoors-v12' ,
203+ accessToken : 'pk.test.token' ,
204+ zoom : 5.72 ,
205+ latitude : 9.503 ,
206+ longitude : - 67.473
207+ } ;
208+
209+ const result = await tool . run ( input ) ;
210+
211+ expect ( result . isError ) . toBe ( false ) ;
212+ const url = ( result . content [ 0 ] as { type : 'text' ; text : string } ) . text ;
213+ expect ( url ) . toContain ( '#5.72/9.503/-67.473' ) ;
214+ } ) ;
215+
216+ it ( 'should not include hash fragment when coordinates are incomplete' , async ( ) => {
217+ // Only zoom provided
218+ const input1 = {
219+ before : 'mapbox/streets-v11' ,
220+ after : 'mapbox/outdoors-v12' ,
221+ accessToken : 'pk.test.token' ,
222+ zoom : 10
223+ } ;
224+
225+ const result1 = await tool . run ( input1 ) ;
226+ expect ( result1 . isError ) . toBe ( false ) ;
227+ const url1 = ( result1 . content [ 0 ] as { type : 'text' ; text : string } ) . text ;
228+ expect ( url1 ) . not . toContain ( '#' ) ;
229+
230+ // Only latitude and longitude, no zoom
231+ const input2 = {
232+ before : 'mapbox/streets-v11' ,
233+ after : 'mapbox/outdoors-v12' ,
234+ accessToken : 'pk.test.token' ,
235+ latitude : 40.7128 ,
236+ longitude : - 74.006
237+ } ;
238+
239+ const result2 = await tool . run ( input2 ) ;
240+ expect ( result2 . isError ) . toBe ( false ) ;
241+ const url2 = ( result2 . content [ 0 ] as { type : 'text' ; text : string } ) . text ;
242+ expect ( url2 ) . not . toContain ( '#' ) ;
243+ } ) ;
244+
245+ it ( 'should handle both nocache and map position together' , async ( ) => {
246+ const input = {
247+ before : 'mapbox/streets-v11' ,
248+ after : 'mapbox/outdoors-v12' ,
249+ accessToken : 'pk.test.token' ,
250+ noCache : true ,
251+ zoom : 12 ,
252+ latitude : 37.7749 ,
253+ longitude : - 122.4194
254+ } ;
255+
256+ const result = await tool . run ( input ) ;
257+
258+ expect ( result . isError ) . toBe ( false ) ;
259+ const url = ( result . content [ 0 ] as { type : 'text' ; text : string } ) . text ;
260+ expect ( url ) . toContain ( 'nocache=true' ) ;
261+ expect ( url ) . toContain ( '#12/37.7749/-122.4194' ) ;
262+ } ) ;
231263 } ) ;
232264
233265 describe ( 'metadata' , ( ) => {
0 commit comments