77 afterAll ,
88 expect ,
99} from "vitest" ;
10+ import fs from "node:fs" ;
11+ import os from "node:os" ;
12+ import path from "node:path" ;
1013import {
1114 E2E_ACCESS_TOKEN ,
1215 SHOULD_SKIP_CONTROL_E2E ,
@@ -21,14 +24,38 @@ import { parseNdjsonLines } from "../../helpers/ndjson.js";
2124describe . skipIf ( SHOULD_SKIP_CONTROL_E2E ) ( "Auth Keys E2E Tests" , ( ) => {
2225 let testAppId : string ;
2326 let teardownApp : ( ( ) => Promise < void > ) | undefined ;
27+ // Temp config directory for switch/current tests that need a local account
28+ let tempConfigDir : string ;
2429
2530 beforeAll ( async ( ) => {
2631 ( { appId : testAppId , teardown : teardownApp } =
2732 await createTestApp ( "e2e-keys-test" ) ) ;
33+
34+ // Create a temp config dir with a test account so switch/current commands
35+ // can use configManager.storeAppKey / getApiKey (requires a local account).
36+ // Uses ABLY_CLI_CONFIG_DIR env var to point the CLI at this config.
37+ tempConfigDir = fs . mkdtempSync ( path . join ( os . tmpdir ( ) , "ably-e2e-keys-" ) ) ;
38+ const tomlConfig = `[current]
39+ account = "e2e-test"
40+
41+ [accounts.e2e-test]
42+ accessToken = "${ E2E_ACCESS_TOKEN || "" } "
43+ accountId = "e2e-test-account"
44+ accountName = "E2E Test Account"
45+ userEmail = "e2e@test.com"
46+ currentAppId = "${ testAppId || "" } "
47+ ` ;
48+ fs . writeFileSync ( path . join ( tempConfigDir , "config" ) , tomlConfig , {
49+ mode : 0o600 ,
50+ } ) ;
2851 } ) ;
2952
3053 afterAll ( async ( ) => {
3154 await teardownApp ?.( ) ;
55+ // Clean up temp config directory
56+ if ( tempConfigDir ) {
57+ fs . rmSync ( tempConfigDir , { recursive : true , force : true } ) ;
58+ }
3259 } ) ;
3360
3461 beforeEach ( ( ) => {
@@ -158,4 +185,143 @@ describe.skipIf(SHOULD_SKIP_CONTROL_E2E)("Auth Keys E2E Tests", () => {
158185 expect ( nameChange ) . toHaveProperty ( "before" , originalName ) ;
159186 expect ( nameChange ) . toHaveProperty ( "after" , updatedName ) ;
160187 } ) ;
188+
189+ it ( "should revoke a key by key name" , { timeout : 20000 } , async ( ) => {
190+ setupTestFailureHandler ( "should revoke a key by key name" ) ;
191+
192+ // First create a key to revoke
193+ const keyName = `e2e-revoke-key-${ Date . now ( ) } ` ;
194+ const createResult = await runCommand (
195+ [ "auth" , "keys" , "create" , keyName , "--app" , testAppId , "--json" ] ,
196+ {
197+ env : { ABLY_ACCESS_TOKEN : E2E_ACCESS_TOKEN || "" } ,
198+ } ,
199+ ) ;
200+
201+ const createRecord = parseNdjsonLines ( createResult . stdout ) . find (
202+ ( r ) => r . type === "result" ,
203+ ) as Record < string , unknown > ;
204+ const createdKey = createRecord . key as Record < string , unknown > ;
205+ const keyFullName = createdKey . keyName as string ;
206+
207+ // Revoke by key name (appId is embedded in keyFullName), --force to skip confirmation
208+ const revokeResult = await runCommand (
209+ [ "auth" , "keys" , "revoke" , keyFullName , "--force" , "--json" ] ,
210+ {
211+ env : { ABLY_ACCESS_TOKEN : E2E_ACCESS_TOKEN || "" } ,
212+ } ,
213+ ) ;
214+
215+ expect ( revokeResult . exitCode ) . toBe ( 0 ) ;
216+ const revokeRecord = parseNdjsonLines ( revokeResult . stdout ) . find (
217+ ( r ) => r . type === "result" ,
218+ ) as Record < string , unknown > ;
219+ expect ( revokeRecord ) . toBeDefined ( ) ;
220+ expect ( revokeRecord ) . toHaveProperty ( "success" , true ) ;
221+ const revokedKey = revokeRecord . key as Record < string , unknown > ;
222+ expect ( revokedKey ) . toHaveProperty ( "keyName" , keyFullName ) ;
223+ expect ( revokedKey ) . toHaveProperty ( "message" , "Key has been revoked" ) ;
224+ } ) ;
225+
226+ it ( "should switch to a key by key name" , { timeout : 20000 } , async ( ) => {
227+ setupTestFailureHandler ( "should switch to a key by key name" ) ;
228+
229+ // Create a key to switch to
230+ const keyName = `e2e-switch-key-${ Date . now ( ) } ` ;
231+ const createResult = await runCommand (
232+ [ "auth" , "keys" , "create" , keyName , "--app" , testAppId , "--json" ] ,
233+ {
234+ env : { ABLY_ACCESS_TOKEN : E2E_ACCESS_TOKEN || "" } ,
235+ } ,
236+ ) ;
237+
238+ const createRecord = parseNdjsonLines ( createResult . stdout ) . find (
239+ ( r ) => r . type === "result" ,
240+ ) as Record < string , unknown > ;
241+ const createdKey = createRecord . key as Record < string , unknown > ;
242+ const keyFullName = createdKey . keyName as string ;
243+
244+ // Switch requires a local account config to store the key.
245+ // Use ABLY_CLI_CONFIG_DIR to point at our temp config with a test account.
246+ const switchResult = await runCommand (
247+ [ "auth" , "keys" , "switch" , keyFullName , "--json" ] ,
248+ {
249+ env : {
250+ ABLY_ACCESS_TOKEN : E2E_ACCESS_TOKEN || "" ,
251+ ABLY_CLI_CONFIG_DIR : tempConfigDir ,
252+ } ,
253+ } ,
254+ ) ;
255+
256+ expect ( switchResult . exitCode ) . toBe ( 0 ) ;
257+ const switchRecord = parseNdjsonLines ( switchResult . stdout ) . find (
258+ ( r ) => r . type === "result" ,
259+ ) as Record < string , unknown > ;
260+ expect ( switchRecord ) . toBeDefined ( ) ;
261+ expect ( switchRecord ) . toHaveProperty ( "success" , true ) ;
262+ const switchedKey = switchRecord . key as Record < string , unknown > ;
263+ expect ( switchedKey ) . toHaveProperty ( "appId" , testAppId ) ;
264+ expect ( switchedKey ) . toHaveProperty ( "keyName" , keyFullName ) ;
265+ expect ( switchedKey ) . toHaveProperty ( "keyLabel" , keyName ) ;
266+ } ) ;
267+
268+ it (
269+ "should show current key after switching" ,
270+ { timeout : 25000 } ,
271+ async ( ) => {
272+ setupTestFailureHandler ( "should show current key after switching" ) ;
273+
274+ // Create a key and switch to it
275+ const keyName = `e2e-current-key-${ Date . now ( ) } ` ;
276+ const createResult = await runCommand (
277+ [ "auth" , "keys" , "create" , keyName , "--app" , testAppId , "--json" ] ,
278+ {
279+ env : { ABLY_ACCESS_TOKEN : E2E_ACCESS_TOKEN || "" } ,
280+ } ,
281+ ) ;
282+
283+ const createRecord = parseNdjsonLines ( createResult . stdout ) . find (
284+ ( r ) => r . type === "result" ,
285+ ) as Record < string , unknown > ;
286+ const createdKey = createRecord . key as Record < string , unknown > ;
287+ const keyFullName = createdKey . keyName as string ;
288+ const keyValue = createdKey . key as string ;
289+
290+ // Switch to the key (writes to temp config)
291+ const switchResult = await runCommand (
292+ [ "auth" , "keys" , "switch" , keyFullName , "--json" ] ,
293+ {
294+ env : {
295+ ABLY_ACCESS_TOKEN : E2E_ACCESS_TOKEN || "" ,
296+ ABLY_CLI_CONFIG_DIR : tempConfigDir ,
297+ } ,
298+ } ,
299+ ) ;
300+ expect ( switchResult . exitCode ) . toBe ( 0 ) ;
301+
302+ // Verify current reads from the same temp config
303+ const currentResult = await runCommand (
304+ [ "auth" , "keys" , "current" , "--app" , testAppId , "--json" ] ,
305+ {
306+ env : {
307+ ABLY_ACCESS_TOKEN : E2E_ACCESS_TOKEN || "" ,
308+ ABLY_CLI_CONFIG_DIR : tempConfigDir ,
309+ } ,
310+ } ,
311+ ) ;
312+
313+ expect ( currentResult . exitCode ) . toBe ( 0 ) ;
314+ const currentRecord = parseNdjsonLines ( currentResult . stdout ) . find (
315+ ( r ) => r . type === "result" ,
316+ ) as Record < string , unknown > ;
317+ expect ( currentRecord ) . toBeDefined ( ) ;
318+ expect ( currentRecord ) . toHaveProperty ( "success" , true ) ;
319+ const currentKey = currentRecord . key as Record < string , unknown > ;
320+ expect ( currentKey ) . toHaveProperty ( "id" , keyFullName ) ;
321+ expect ( currentKey ) . toHaveProperty ( "value" , keyValue ) ;
322+ expect ( currentKey ) . toHaveProperty ( "label" , keyName ) ;
323+ const app = currentKey . app as Record < string , unknown > ;
324+ expect ( app ) . toHaveProperty ( "id" , testAppId ) ;
325+ } ,
326+ ) ;
161327} ) ;
0 commit comments