@@ -23,17 +23,23 @@ public void SetupMocksAndUriHandler()
2323_mUserNotifier = new Mock < IUserNotifier > ( MockBehavior . Strict ) ;
2424_mRdpConnector = new Mock < IRdpConnector > ( MockBehavior . Strict ) ;
2525_mRpcController = new Mock < IRpcController > ( MockBehavior . Strict ) ;
26+ _mCredentialManager = new Mock < ICredentialManager > ( MockBehavior . Strict ) ;
2627
27- uriHandler = new UriHandler ( logger , _mRpcController . Object , _mUserNotifier . Object , _mRdpConnector . Object ) ;
28+ uriHandler = new UriHandler ( logger ,
29+ _mRpcController . Object ,
30+ _mUserNotifier . Object ,
31+ _mRdpConnector . Object ,
32+ _mCredentialManager . Object ) ;
2833}
2934
3035private Mock < IUserNotifier > _mUserNotifier ;
3136private Mock < IRdpConnector > _mRdpConnector ;
3237private Mock < IRpcController > _mRpcController ;
38+ private Mock < ICredentialManager > _mCredentialManager ;
3339private UriHandler uriHandler ; // Unit under test.
3440
3541[ SetUp ]
36- public void AgentAndWorkspaceFixtures ( )
42+ public void AgentWorkspaceAndCredentialFixtures ( )
3743{
3844agent11 = new Agent ( ) ;
3945agent11 . Fqdn . Add ( "workspace1.coder" ) ;
@@ -54,94 +60,116 @@ public void AgentAndWorkspaceFixtures()
5460Workspaces = [ workspace1 ] ,
5561Agents = [ agent11 ] ,
5662} ;
63+
64+ credentialModel1 = new CredentialModel
65+ {
66+ State = CredentialState . Valid ,
67+ CoderUrl = "https://coder.test" ,
68+ } ;
5769}
5870
5971private Agent agent11 ;
6072private Workspace workspace1 ;
6173private RpcModel modelWithWorkspace1 ;
74+ private CredentialModel credentialModel1 ;
6275
6376[ Test ( Description = "Open RDP with username & password" ) ]
6477[ CancelAfter ( 30_000 ) ]
6578public async Task Mainline ( CancellationToken ct )
6679{
67- var input = new Uri ( "coder:/v0/open/ws/workspace1/agent/agent11/rdp?username=testy&password=sesame" ) ;
80+ var input = new Uri (
81+ "coder://coder.test/v0/open/ws/workspace1/agent/agent11/rdp?username=testy&password=sesame" ) ;
6882
83+ _mCredentialManager . Setup ( m=> m . GetCachedCredentials ( ) ) . Returns ( credentialModel1 ) ;
6984_mRpcController . Setup ( m=> m . GetState ( ) ) . Returns ( modelWithWorkspace1 ) ;
7085var expectedCred = new RdpCredentials ( "testy" , "sesame" ) ;
7186_ = _mRdpConnector . Setup ( m=> m . WriteCredentials ( agent11 . Fqdn [ 0 ] , expectedCred ) ) ;
7287_ = _mRdpConnector . Setup ( m=> m . Connect ( agent11 . Fqdn [ 0 ] , IRdpConnector . DefaultPort , ct ) )
7388. Returns ( Task . CompletedTask ) ;
7489await uriHandler . HandleUri ( input , ct ) ;
90+ _mRdpConnector . Verify ( m=> m . WriteCredentials ( It . IsAny < string > ( ) , It . IsAny < RdpCredentials > ( ) ) ) ;
91+ _mRdpConnector . Verify ( m=> m . Connect ( It . IsAny < string > ( ) , It . IsAny < int > ( ) , ct ) , Times . Once ) ;
7592}
7693
7794[ Test ( Description = "Open RDP with no credentials" ) ]
7895[ CancelAfter ( 30_000 ) ]
7996public async Task NoCredentials ( CancellationToken ct )
8097{
81- var input = new Uri ( "coder:/v0/open/ws/workspace1/agent/agent11/rdp" ) ;
98+ var input = new Uri ( "coder://coder.test/ v0/open/ws/workspace1/agent/agent11/rdp" ) ;
8299
100+ _mCredentialManager . Setup ( m=> m . GetCachedCredentials ( ) ) . Returns ( credentialModel1 ) ;
83101_mRpcController . Setup ( m=> m . GetState ( ) ) . Returns ( modelWithWorkspace1 ) ;
84102_ = _mRdpConnector . Setup ( m=> m . Connect ( agent11 . Fqdn [ 0 ] , IRdpConnector . DefaultPort , ct ) )
85103. Returns ( Task . CompletedTask ) ;
86104await uriHandler . HandleUri ( input , ct ) ;
105+ _mRdpConnector . Verify ( m=> m . Connect ( It . IsAny < string > ( ) , It . IsAny < int > ( ) , ct ) , Times . Once ) ;
87106}
88107
89108[ Test ( Description = "Unknown app slug" ) ]
90109[ CancelAfter ( 30_000 ) ]
91110public async Task UnknownApp ( CancellationToken ct )
92111{
93- var input = new Uri ( "coder:/v0/open/ws/workspace1/agent/agent11/someapp" ) ;
112+ var input = new Uri ( "coder://coder.test/ v0/open/ws/workspace1/agent/agent11/someapp" ) ;
94113
114+ _mCredentialManager . Setup ( m=> m . GetCachedCredentials ( ) ) . Returns ( credentialModel1 ) ;
95115_mRpcController . Setup ( m=> m . GetState ( ) ) . Returns ( modelWithWorkspace1 ) ;
96116_mUserNotifier . Setup ( m=> m . ShowErrorNotification ( It . IsAny < string > ( ) , It . IsRegex ( "someapp" ) , ct ) )
97117. Returns ( Task . CompletedTask ) ;
98118await uriHandler . HandleUri ( input , ct ) ;
119+ _mUserNotifier . Verify ( m=> m . ShowErrorNotification ( It . IsAny < string > ( ) , It . IsAny < string > ( ) , ct ) , Times . Once ( ) ) ;
99120}
100121
101122[ Test ( Description = "Unknown agent name" ) ]
102123[ CancelAfter ( 30_000 ) ]
103124public async Task UnknownAgent ( CancellationToken ct )
104125{
105- var input = new Uri ( "coder:/v0/open/ws/workspace1/agent/wrongagent/rdp" ) ;
126+ var input = new Uri ( "coder://coder.test/ v0/open/ws/workspace1/agent/wrongagent/rdp" ) ;
106127
128+ _mCredentialManager . Setup ( m=> m . GetCachedCredentials ( ) ) . Returns ( credentialModel1 ) ;
107129_mRpcController . Setup ( m=> m . GetState ( ) ) . Returns ( modelWithWorkspace1 ) ;
108130_mUserNotifier . Setup ( m=> m . ShowErrorNotification ( It . IsAny < string > ( ) , It . IsRegex ( "wrongagent" ) , ct ) )
109131. Returns ( Task . CompletedTask ) ;
110132await uriHandler . HandleUri ( input , ct ) ;
133+ _mUserNotifier . Verify ( m=> m . ShowErrorNotification ( It . IsAny < string > ( ) , It . IsAny < string > ( ) , ct ) , Times . Once ( ) ) ;
111134}
112135
113136[ Test ( Description = "Unknown workspace name" ) ]
114137[ CancelAfter ( 30_000 ) ]
115138public async Task UnknownWorkspace ( CancellationToken ct )
116139{
117- var input = new Uri ( "coder:/v0/open/ws/wrongworkspace/agent/agent11/rdp" ) ;
140+ var input = new Uri ( "coder://coder.test/ v0/open/ws/wrongworkspace/agent/agent11/rdp" ) ;
118141
142+ _mCredentialManager . Setup ( m=> m . GetCachedCredentials ( ) ) . Returns ( credentialModel1 ) ;
119143_mRpcController . Setup ( m=> m . GetState ( ) ) . Returns ( modelWithWorkspace1 ) ;
120144_mUserNotifier . Setup ( m=> m . ShowErrorNotification ( It . IsAny < string > ( ) , It . IsRegex ( "wrongworkspace" ) , ct ) )
121145. Returns ( Task . CompletedTask ) ;
122146await uriHandler . HandleUri ( input , ct ) ;
147+ _mUserNotifier . Verify ( m=> m . ShowErrorNotification ( It . IsAny < string > ( ) , It . IsAny < string > ( ) , ct ) , Times . Once ( ) ) ;
123148}
124149
125150[ Test ( Description = "Malformed Query String" ) ]
126151[ CancelAfter ( 30_000 ) ]
127152public async Task MalformedQuery ( CancellationToken ct )
128153{
129154// there might be some query string that gets the parser to throw an exception, but I could not find one.
130- var input = new Uri ( "coder:/v0/open/ws/workspace1/agent/agent11/rdp?%&##" ) ;
155+ var input = new Uri ( "coder://coder.test/ v0/open/ws/workspace1/agent/agent11/rdp?%&##" ) ;
131156
157+ _mCredentialManager . Setup ( m=> m . GetCachedCredentials ( ) ) . Returns ( credentialModel1 ) ;
132158_mRpcController . Setup ( m=> m . GetState ( ) ) . Returns ( modelWithWorkspace1 ) ;
133159// treated the same as if we just didn't include credentials
134160_ = _mRdpConnector . Setup ( m=> m . Connect ( agent11 . Fqdn [ 0 ] , IRdpConnector . DefaultPort , ct ) )
135161. Returns ( Task . CompletedTask ) ;
136162await uriHandler . HandleUri ( input , ct ) ;
163+ _mRdpConnector . Verify ( m=> m . Connect ( It . IsAny < string > ( ) , It . IsAny < int > ( ) , ct ) , Times . Once ) ;
137164}
138165
139166[ Test ( Description = "VPN not started" ) ]
140167[ CancelAfter ( 30_000 ) ]
141168public async Task VPNNotStarted ( CancellationToken ct )
142169{
143- var input = new Uri ( "coder:/v0/open/ws/wrongworkspace/agent/agent11/rdp" ) ;
170+ var input = new Uri ( "coder://coder.test/ v0/open/ws/wrongworkspace/agent/agent11/rdp" ) ;
144171
172+ _mCredentialManager . Setup ( m=> m . GetCachedCredentials ( ) ) . Returns ( credentialModel1 ) ;
145173_mRpcController . Setup ( m=> m . GetState ( ) ) . Returns ( new RpcModel
146174{
147175VpnLifecycle = VpnLifecycle . Starting ,
@@ -150,29 +178,79 @@ public async Task VPNNotStarted(CancellationToken ct)
150178_mUserNotifier . Setup ( m=> m . ShowErrorNotification ( It . IsAny < string > ( ) , It . IsRegex ( "Coder Connect" ) , ct ) )
151179. Returns ( Task . CompletedTask ) ;
152180await uriHandler . HandleUri ( input , ct ) ;
181+ _mUserNotifier . Verify ( m=> m . ShowErrorNotification ( It . IsAny < string > ( ) , It . IsAny < string > ( ) , ct ) , Times . Once ( ) ) ;
153182}
154183
155184[ Test ( Description = "Wrong number of components" ) ]
156185[ CancelAfter ( 30_000 ) ]
157186public async Task UnknownNumComponents ( CancellationToken ct )
158187{
159- var input = new Uri ( "coder:/v0/open/ws/wrongworkspace/agent11/rdp" ) ;
188+ var input = new Uri ( "coder://coder.test/ v0/open/ws/wrongworkspace/agent11/rdp" ) ;
160189
190+ _mCredentialManager . Setup ( m=> m . GetCachedCredentials ( ) ) . Returns ( credentialModel1 ) ;
161191_mRpcController . Setup ( m=> m . GetState ( ) ) . Returns ( modelWithWorkspace1 ) ;
162192_mUserNotifier . Setup ( m=> m . ShowErrorNotification ( It . IsAny < string > ( ) , It . IsAny < string > ( ) , ct ) )
163193. Returns ( Task . CompletedTask ) ;
164194await uriHandler . HandleUri ( input , ct ) ;
195+ _mUserNotifier . Verify ( m=> m . ShowErrorNotification ( It . IsAny < string > ( ) , It . IsAny < string > ( ) , ct ) , Times . Once ( ) ) ;
165196}
166197
167198[ Test ( Description = "Unknown prefix" ) ]
168199[ CancelAfter ( 30_000 ) ]
169200public async Task UnknownPrefix ( CancellationToken ct )
170201{
171- var input = new Uri ( "coder:/v300/open/ws/workspace1/agent/agent11/rdp" ) ;
202+ var input = new Uri ( "coder://coder.test/ v300/open/ws/workspace1/agent/agent11/rdp" ) ;
172203
204+ _mCredentialManager . Setup ( m=> m . GetCachedCredentials ( ) ) . Returns ( credentialModel1 ) ;
173205_mRpcController . Setup ( m=> m . GetState ( ) ) . Returns ( modelWithWorkspace1 ) ;
174206_mUserNotifier . Setup ( m=> m . ShowErrorNotification ( It . IsAny < string > ( ) , It . IsAny < string > ( ) , ct ) )
175207. Returns ( Task . CompletedTask ) ;
176208await uriHandler . HandleUri ( input , ct ) ;
209+ _mUserNotifier . Verify ( m=> m . ShowErrorNotification ( It . IsAny < string > ( ) , It . IsAny < string > ( ) , ct ) , Times . Once ( ) ) ;
210+ }
211+
212+ [ Test ( Description = "Unknown authority" ) ]
213+ [ CancelAfter ( 30_000 ) ]
214+ public async Task UnknownAuthority ( CancellationToken ct )
215+ {
216+ var input = new Uri ( "coder://unknown.test/v0/open/ws/workspace1/agent/agent11/rdp" ) ;
217+
218+ _mCredentialManager . Setup ( m=> m . GetCachedCredentials ( ) ) . Returns ( credentialModel1 ) ;
219+ _mRpcController . Setup ( m=> m . GetState ( ) ) . Returns ( modelWithWorkspace1 ) ;
220+ _mUserNotifier . Setup ( m=> m . ShowErrorNotification ( It . IsAny < string > ( ) , It . IsRegex ( @"unknown\.test" ) , ct ) )
221+ . Returns ( Task . CompletedTask ) ;
222+ await uriHandler . HandleUri ( input , ct ) ;
223+ _mUserNotifier . Verify ( m=> m . ShowErrorNotification ( It . IsAny < string > ( ) , It . IsAny < string > ( ) , ct ) , Times . Once ( ) ) ;
224+ }
225+
226+ [ Test ( Description = "Missing authority" ) ]
227+ [ CancelAfter ( 30_000 ) ]
228+ public async Task MissingAuthority ( CancellationToken ct )
229+ {
230+ var input = new Uri ( "coder:/v0/open/ws/workspace1/agent/agent11/rdp" ) ;
231+
232+ _mCredentialManager . Setup ( m=> m . GetCachedCredentials ( ) ) . Returns ( credentialModel1 ) ;
233+ _mRpcController . Setup ( m=> m . GetState ( ) ) . Returns ( modelWithWorkspace1 ) ;
234+ _mUserNotifier . Setup ( m=> m . ShowErrorNotification ( It . IsAny < string > ( ) , It . IsRegex ( "Coder server" ) , ct ) )
235+ . Returns ( Task . CompletedTask ) ;
236+ await uriHandler . HandleUri ( input , ct ) ;
237+ _mUserNotifier . Verify ( m=> m . ShowErrorNotification ( It . IsAny < string > ( ) , It . IsAny < string > ( ) , ct ) , Times . Once ( ) ) ;
238+ }
239+
240+ [ Test ( Description = "Not signed in" ) ]
241+ [ CancelAfter ( 30_000 ) ]
242+ public async Task NotSignedIn ( CancellationToken ct )
243+ {
244+ var input = new Uri ( "coder://coder.test/v0/open/ws/workspace1/agent/agent11/rdp" ) ;
245+
246+ _mCredentialManager . Setup ( m=> m . GetCachedCredentials ( ) ) . Returns ( new CredentialModel ( )
247+ {
248+ State = CredentialState . Invalid ,
249+ } ) ;
250+ _mRpcController . Setup ( m=> m . GetState ( ) ) . Returns ( modelWithWorkspace1 ) ;
251+ _mUserNotifier . Setup ( m=> m . ShowErrorNotification ( It . IsAny < string > ( ) , It . IsRegex ( "signed in" ) , ct ) )
252+ . Returns ( Task . CompletedTask ) ;
253+ await uriHandler . HandleUri ( input , ct ) ;
254+ _mUserNotifier . Verify ( m=> m . ShowErrorNotification ( It . IsAny < string > ( ) , It . IsAny < string > ( ) , ct ) , Times . Once ( ) ) ;
177255}
178256}