Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit12c7c98

Browse files
committed
Refactoring and added tests
1 parentfe96eb5 commit12c7c98

File tree

6 files changed

+99
-24
lines changed

6 files changed

+99
-24
lines changed

‎src/commands.ts‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -219,13 +219,13 @@ export class Commands {
219219
this.restClient.setHost(url);
220220
this.restClient.setSessionToken(res.token);
221221

222-
// Store on disk to be used by the cli.
223-
awaitthis.cliManager.configure(label,url,res.token);
224-
225222
// Store these to be used in later sessions.
226223
awaitthis.mementoManager.setUrl(url);
227224
awaitthis.secretsManager.setSessionToken(res.token);
228225

226+
// Store on disk to be used by the cli.
227+
awaitthis.cliManager.configure(label,url,res.token);
228+
229229
// These contexts control various menu items and the sidebar.
230230
this.contextManager.set("coder.authenticated",true);
231231
if(res.user.roles.find((role)=>role.name==="owner")){
@@ -247,9 +247,9 @@ export class Commands {
247247
}
248248
});
249249

250+
awaitthis.secretsManager.triggerLoginStateChange("login");
250251
// Fetch workspaces for the new deployment.
251252
vscode.commands.executeCommand("coder.refreshWorkspaces");
252-
this.secretsManager.triggerLoginStateChange("login");
253253
}
254254

255255
/**
@@ -403,9 +403,9 @@ export class Commands {
403403
}
404404
});
405405

406+
awaitthis.secretsManager.triggerLoginStateChange("logout");
406407
// This will result in clearing the workspace list.
407408
vscode.commands.executeCommand("coder.refreshWorkspaces");
408-
this.secretsManager.triggerLoginStateChange("logout");
409409
}
410410

411411
/**

‎src/core/secretsManager.ts‎

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,9 @@ const SESSION_TOKEN_KEY = "sessionToken";
55
constLOGIN_STATE_KEY="loginState";
66

77
typeAuthAction="login"|"logout";
8+
89
exportclassSecretsManager{
9-
constructor(privatereadonlysecrets:SecretStorage){
10-
voidthis.secrets.delete(LOGIN_STATE_KEY);
11-
}
10+
constructor(privatereadonlysecrets:SecretStorage){}
1211

1312
/**
1413
* Set or unset the last used token.
@@ -34,17 +33,34 @@ export class SecretsManager {
3433
}
3534
}
3635

37-
publictriggerLoginStateChange(action:AuthAction):void{
38-
this.secrets.store(LOGIN_STATE_KEY,action);
36+
/**
37+
* Triggers a login/logout event that propagates across all VS Code windows.
38+
* Uses the secrets storage onDidChange event as a cross-window communication mechanism.
39+
* Appends a timestamp to ensure the value always changes, guaranteeing the event fires.
40+
*/
41+
publicasynctriggerLoginStateChange(action:AuthAction):Promise<void>{
42+
constdate=newDate().toISOString();
43+
awaitthis.secrets.store(LOGIN_STATE_KEY,`${action}-${date}`);
3944
}
4045

46+
/**
47+
* Listens for login/logout events from any VS Code window.
48+
* The secrets storage onDidChange event fires across all windows, enabling cross-window sync.
49+
*/
4150
publiconDidChangeLoginState(
4251
listener:(state?:AuthAction)=>Promise<void>,
4352
):Disposable{
4453
returnthis.secrets.onDidChange(async(e)=>{
4554
if(e.key===LOGIN_STATE_KEY){
4655
conststate=awaitthis.secrets.get(LOGIN_STATE_KEY);
47-
listener(stateasAuthAction|undefined);
56+
if(state?.startsWith("login")){
57+
listener("login");
58+
}elseif(state?.startsWith("logout")){
59+
listener("logout");
60+
}else{
61+
// Secret was deleted or is invalid
62+
listener(undefined);
63+
}
4864
}
4965
});
5066
}

‎src/extension.ts‎

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -169,14 +169,14 @@ export async function activate(ctx: vscode.ExtensionContext): Promise<void> {
169169
?params.get("token")
170170
:(params.get("token")??"");
171171

172-
// Store on disk to be used by the cli.
173-
awaitcliManager.configure(toSafeHost(url),url,token);
174-
175172
if(token){
176173
client.setSessionToken(token);
177174
awaitsecretsManager.setSessionToken(token);
178175
}
179176

177+
// Store on disk to be used by the cli.
178+
awaitcliManager.configure(toSafeHost(url),url,token);
179+
180180
vscode.commands.executeCommand(
181181
"coder.open",
182182
owner,
@@ -334,7 +334,6 @@ export async function activate(ctx: vscode.ExtensionContext): Promise<void> {
334334
ctx.subscriptions.push(
335335
secretsManager.onDidChangeLoginState(async(state)=>{
336336
if(state===undefined){
337-
// Initalization - Ignore those events
338337
return;
339338
}
340339

‎src/remote/remote.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ export class Remote {
6161
privatereadonlycliManager:CliManager;
6262
privatereadonlycontextManager:ContextManager;
6363

64-
// Used to race between the login dialog andthelogging in from a different window
64+
// Used to race between the login dialog and logging in from a different window
6565
privateloginDetectedResolver:(()=>void)|undefined;
6666
privateloginDetectedRejector:((reason?:Error)=>void)|undefined;
6767
privateloginDetectedPromise:Promise<void>=Promise.resolve();

‎test/mocks/testHelpers.ts‎

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -234,10 +234,19 @@ export class InMemoryMemento implements vscode.Memento {
234234
exportclassInMemorySecretStorageimplementsvscode.SecretStorage{
235235
privatesecrets=newMap<string,string>();
236236
privateisCorrupted=false;
237-
238-
onDidChange:vscode.Event<vscode.SecretStorageChangeEvent>=()=>({
239-
dispose:()=>{},
240-
});
237+
privatelisteners:Array<(e:vscode.SecretStorageChangeEvent)=>void>=[];
238+
239+
onDidChange:vscode.Event<vscode.SecretStorageChangeEvent>=(listener)=>{
240+
this.listeners.push(listener);
241+
return{
242+
dispose:()=>{
243+
constindex=this.listeners.indexOf(listener);
244+
if(index>-1){
245+
this.listeners.splice(index,1);
246+
}
247+
},
248+
};
249+
};
241250

242251
asyncget(key:string):Promise<string|undefined>{
243252
if(this.isCorrupted){
@@ -250,17 +259,30 @@ export class InMemorySecretStorage implements vscode.SecretStorage {
250259
if(this.isCorrupted){
251260
returnPromise.reject(newError("Storage corrupted"));
252261
}
262+
constoldValue=this.secrets.get(key);
253263
this.secrets.set(key,value);
264+
if(oldValue!==value){
265+
this.fireChangeEvent(key);
266+
}
254267
}
255268

256269
asyncdelete(key:string):Promise<void>{
257270
if(this.isCorrupted){
258271
returnPromise.reject(newError("Storage corrupted"));
259272
}
273+
consthadKey=this.secrets.has(key);
260274
this.secrets.delete(key);
275+
if(hadKey){
276+
this.fireChangeEvent(key);
277+
}
261278
}
262279

263280
corruptStorage():void{
264281
this.isCorrupted=true;
265282
}
283+
284+
privatefireChangeEvent(key:string):void{
285+
constevent:vscode.SecretStorageChangeEvent={ key};
286+
this.listeners.forEach((listener)=>listener(event));
287+
}
266288
}

‎test/unit/core/secretsManager.test.ts‎

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import{beforeEach,describe,expect,it}from"vitest";
1+
import{beforeEach,describe,expect,it,vi}from"vitest";
22

33
import{SecretsManager}from"@/core/secretsManager";
44

@@ -13,7 +13,7 @@ describe("SecretsManager", () => {
1313
secretsManager=newSecretsManager(secretStorage);
1414
});
1515

16-
describe("setSessionToken",()=>{
16+
describe("session token",()=>{
1717
it("should store and retrieve tokens",async()=>{
1818
awaitsecretsManager.setSessionToken("test-token");
1919
expect(awaitsecretsManager.getSessionToken()).toBe("test-token");
@@ -31,14 +31,52 @@ describe("SecretsManager", () => {
3131
awaitsecretsManager.setSessionToken(undefined);
3232
expect(awaitsecretsManager.getSessionToken()).toBeUndefined();
3333
});
34-
});
3534

36-
describe("getSessionToken",()=>{
3735
it("should return undefined for corrupted storage",async()=>{
3836
awaitsecretStorage.store("sessionToken","valid-token");
3937
secretStorage.corruptStorage();
4038

4139
expect(awaitsecretsManager.getSessionToken()).toBeUndefined();
4240
});
4341
});
42+
43+
describe("login state",()=>{
44+
it("should trigger login events",async()=>{
45+
constevents:Array<string|undefined>=[];
46+
secretsManager.onDidChangeLoginState((state)=>{
47+
events.push(state);
48+
returnPromise.resolve();
49+
});
50+
51+
awaitsecretsManager.triggerLoginStateChange("login");
52+
expect(events).toEqual(["login"]);
53+
});
54+
55+
it("should trigger logout events",async()=>{
56+
constevents:Array<string|undefined>=[];
57+
secretsManager.onDidChangeLoginState((state)=>{
58+
events.push(state);
59+
returnPromise.resolve();
60+
});
61+
62+
awaitsecretsManager.triggerLoginStateChange("logout");
63+
expect(events).toEqual(["logout"]);
64+
});
65+
66+
it("should fire same event twice in a row",async()=>{
67+
vi.useFakeTimers();
68+
constevents:Array<string|undefined>=[];
69+
secretsManager.onDidChangeLoginState((state)=>{
70+
events.push(state);
71+
returnPromise.resolve();
72+
});
73+
74+
awaitsecretsManager.triggerLoginStateChange("login");
75+
vi.advanceTimersByTime(5);
76+
awaitsecretsManager.triggerLoginStateChange("login");
77+
78+
expect(events).toEqual(["login","login"]);
79+
vi.useRealTimers();
80+
});
81+
});
4482
});

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp