11// Copyright (c) Microsoft Corporation.
22// Licensed under the MIT License.
33
4+ using System . IO . Pipes ;
45using System . Runtime . InteropServices ;
56using System . Threading . Tasks ;
67using Bicep . Core . FileSystem ;
@@ -22,12 +23,12 @@ public class BicepClientTests
2223 [ TestInitialize ]
2324 public async Task TestInitialize ( )
2425 {
25- var clientFactory = new BicepClientFactory ( new ( ) ) ;
26+ var clientFactory = new BicepClientFactory ( ) ;
2627 var cliName = RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) ? "bicep.exe" : "bicep" ;
2728 var cliPath = Path . GetFullPath ( Path . Combine ( typeof ( BicepClientTests ) . Assembly . Location , $ "../{ cliName } ") ) ;
2829
29- Bicep = await clientFactory . InitializeFromPath (
30- cliPath ,
30+ Bicep = await clientFactory . Initialize (
31+ new ( ) { ExistingCliPath = cliPath } ,
3132 TestContext . CancellationTokenSource . Token ) ;
3233 }
3334
@@ -82,7 +83,7 @@ public async Task Download_fetches_and_installs_bicep_cli(string name, Architect
8283
8384 var bicepCliPath = await clientFactory . Download ( new ( )
8485 {
85- InstallPath = outputDir ,
86+ InstallBasePath = outputDir ,
8687 OsPlatform = osPlatform ,
8788 Architecture = architecture ,
8889 } , TestContext . CancellationTokenSource . Token ) ;
@@ -93,22 +94,121 @@ public async Task Download_fetches_and_installs_bicep_cli(string name, Architect
9394 }
9495
9596 [ TestMethod ]
96- public async Task DownloadAndInitialize_validates_version_number_format ( )
97+ public async Task Initialize_validates_version_number_format ( )
9798 {
98- var clientFactory = new BicepClientFactory ( new ( ) ) ;
99- await FluentActions . Invoking ( ( ) => clientFactory . DownloadAndInitialize ( new ( ) { BicepVersion = "v0.1.1" } , default ) )
99+ var clientFactory = new BicepClientFactory ( ) ;
100+ await FluentActions . Invoking ( ( ) => clientFactory . Initialize ( new ( ) { BicepVersion = "v0.1.1" } , default ) )
100101 . Should ( ) . ThrowAsync < ArgumentException > ( ) . WithMessage ( "Invalid Bicep version format 'v0.1.1'. Expected format: 'x.y.z' where x, y, and z are integers." ) ;
101102 }
102103
104+ [ DataTestMethod ]
105+ [ DataRow ( "1.2" , "Invalid Bicep version format '1.2'. Expected format: 'x.y.z' where x, y, and z are integers." ) ]
106+ [ DataRow ( "v1.2.3" , "Invalid Bicep version format 'v1.2.3'. Expected format: 'x.y.z' where x, y, and z are integers." ) ]
107+ [ DataRow ( "1.2.3.4" , "Invalid Bicep version format '1.2.3.4'. Expected format: 'x.y.z' where x, y, and z are integers." ) ]
108+ [ DataRow ( "latest" , "Invalid Bicep version format 'latest'. Expected format: 'x.y.z' where x, y, and z are integers." ) ]
109+ public void Validate_throws_for_invalid_BicepVersion ( string version , string expectedMessage )
110+ {
111+ FluentActions . Invoking ( ( ) => BicepClientConfiguration . Validate ( new ( ) { BicepVersion = version } ) )
112+ . Should ( ) . Throw < ArgumentException > ( ) . WithMessage ( expectedMessage ) ;
113+ }
114+
115+ [ DataTestMethod ]
116+ [ DataRow ( "1.2.3" ) ]
117+ [ DataRow ( "0.0.0" ) ]
118+ [ DataRow ( "100.200.300" ) ]
119+ public void Validate_accepts_valid_BicepVersion ( string version )
120+ {
121+ FluentActions . Invoking ( ( ) => BicepClientConfiguration . Validate ( new ( ) { BicepVersion = version } ) )
122+ . Should ( ) . NotThrow ( ) ;
123+ }
124+
125+ [ TestMethod ]
126+ public void Validate_throws_when_ExistingCliPath_combined_with_InstallBasePath ( )
127+ {
128+ FluentActions . Invoking ( ( ) => BicepClientConfiguration . Validate ( new ( ) { ExistingCliPath = "/some/path" , InstallBasePath = "/some/base" } ) )
129+ . Should ( ) . Throw < ArgumentException > ( ) . WithMessage ( "*ExistingCliPath*InstallBasePath*" ) ;
130+ }
131+
132+ [ TestMethod ]
133+ public void Validate_throws_when_ExistingCliPath_combined_with_BicepVersion ( )
134+ {
135+ FluentActions . Invoking ( ( ) => BicepClientConfiguration . Validate ( new ( ) { ExistingCliPath = "/some/path" , BicepVersion = "1.0.0" } ) )
136+ . Should ( ) . Throw < ArgumentException > ( ) . WithMessage ( "*ExistingCliPath*BicepVersion*" ) ;
137+ }
138+
139+ [ TestMethod ]
140+ public void Validate_throws_when_ExistingCliPath_combined_with_OsPlatform ( )
141+ {
142+ FluentActions . Invoking ( ( ) => BicepClientConfiguration . Validate ( new ( ) { ExistingCliPath = "/some/path" , OsPlatform = OSPlatform . Linux } ) )
143+ . Should ( ) . Throw < ArgumentException > ( ) . WithMessage ( "*ExistingCliPath*OsPlatform*" ) ;
144+ }
145+
146+ [ TestMethod ]
147+ public void Validate_throws_when_ExistingCliPath_combined_with_Architecture ( )
148+ {
149+ FluentActions . Invoking ( ( ) => BicepClientConfiguration . Validate ( new ( ) { ExistingCliPath = "/some/path" , Architecture = Architecture . X64 } ) )
150+ . Should ( ) . Throw < ArgumentException > ( ) . WithMessage ( "*ExistingCliPath*Architecture*" ) ;
151+ }
152+
153+ [ TestMethod ]
154+ public void Validate_throws_when_Stdio_combined_with_ConnectionTimeout ( )
155+ {
156+ FluentActions . Invoking ( ( ) => BicepClientConfiguration . Validate ( new ( ) { ConnectionMode = BicepConnectionMode . Stdio , ConnectionTimeout = TimeSpan . FromSeconds ( 10 ) } ) )
157+ . Should ( ) . Throw < ArgumentException > ( ) . WithMessage ( "*ConnectionTimeout*Stdio*" ) ;
158+ }
159+
160+ [ TestMethod ]
161+ public void Validate_accepts_Stdio_without_ConnectionTimeout ( )
162+ {
163+ FluentActions . Invoking ( ( ) => BicepClientConfiguration . Validate ( new ( ) { ConnectionMode = BicepConnectionMode . Stdio } ) )
164+ . Should ( ) . NotThrow ( ) ;
165+ }
166+
167+ [ TestMethod ]
168+ public void Validate_accepts_Stdio_with_ExistingCliPath ( )
169+ {
170+ var existingPath = typeof ( BicepClientTests ) . Assembly . Location ;
171+ FluentActions . Invoking ( ( ) => BicepClientConfiguration . Validate ( new ( ) { ConnectionMode = BicepConnectionMode . Stdio , ExistingCliPath = existingPath } ) )
172+ . Should ( ) . NotThrow ( ) ;
173+ }
174+
103175 [ TestMethod ]
104- public async Task DownloadAndInitialize_validates_path_existence ( )
176+ public async Task Initialize_validates_path_existence ( )
105177 {
106178 var nonExistentPath = FileHelper . GetUniqueTestOutputPath ( TestContext ) ;
107- var clientFactory = new BicepClientFactory ( new ( ) ) ;
108- await FluentActions . Invoking ( ( ) => clientFactory . InitializeFromPath ( nonExistentPath , default ) )
179+ var clientFactory = new BicepClientFactory ( ) ;
180+ await FluentActions . Invoking ( ( ) => clientFactory . Initialize ( new ( ) { ExistingCliPath = nonExistentPath } , default ) )
109181 . Should ( ) . ThrowAsync < FileNotFoundException > ( ) . WithMessage ( $ "The specified Bicep CLI path does not exist: '{ nonExistentPath } '.") ;
110182 }
111183
184+ [ TestMethod ]
185+ public void Validate_throws_when_ExistingCliPath_does_not_exist ( )
186+ {
187+ var nonExistentPath = Path . Combine ( Path . GetTempPath ( ) , Guid . NewGuid ( ) . ToString ( ) ) ;
188+ FluentActions . Invoking ( ( ) => BicepClientConfiguration . Validate ( new ( ) { ExistingCliPath = nonExistentPath } ) )
189+ . Should ( ) . Throw < FileNotFoundException > ( ) . WithMessage ( $ "The specified Bicep CLI path does not exist: '{ nonExistentPath } '.") ;
190+ }
191+
192+ [ TestMethod ]
193+ public async Task Initialize_throws_NotSupportedException_for_unsupported_ConnectionMode ( )
194+ {
195+ var existingPath = typeof ( BicepClientTests ) . Assembly . Location ;
196+ var clientFactory = new BicepClientFactory ( ) ;
197+ // Cast an unknown value to exercise the default branch in the factory's switch expression.
198+ var unknownMode = ( BicepConnectionMode ) 99 ;
199+ await FluentActions . Invoking ( ( ) => clientFactory . Initialize ( new ( ) { ExistingCliPath = existingPath , ConnectionMode = unknownMode } , default ) )
200+ . Should ( ) . ThrowAsync < NotSupportedException > ( ) ;
201+ }
202+
203+ [ TestMethod ]
204+ public async Task WaitForPipeConnection_throws_timeout_exception_when_connection_times_out ( )
205+ {
206+ using var pipeStream = new NamedPipeServerStream ( Guid . NewGuid ( ) . ToString ( ) , PipeDirection . InOut , 1 , PipeTransmissionMode . Byte , PipeOptions . Asynchronous ) ;
207+
208+ await FluentActions . Invoking ( ( ) => BicepClient . WaitForPipeConnection ( pipeStream , TimeSpan . FromMilliseconds ( 500 ) , CancellationToken . None ) )
209+ . Should ( ) . ThrowAsync < TimeoutException > ( ) . WithMessage ( "Timed out waiting for the Bicep CLI process to connect after * seconds." ) ;
210+ }
211+
112212 [ TestMethod ]
113213 public void BuildDownloadUrlForTag_returns_correct_url ( )
114214 {
@@ -138,6 +238,28 @@ param location string
138238 result . Diagnostics . Should ( ) . Contain ( x => x . Code == "no-unused-params" ) ;
139239 }
140240
241+ [ TestMethod ]
242+ public async Task Compile_runs_successfully_with_stdio ( )
243+ {
244+ var clientFactory = new BicepClientFactory ( ) ;
245+ var cliName = RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) ? "bicep.exe" : "bicep" ;
246+ var cliPath = Path . GetFullPath ( Path . Combine ( typeof ( BicepClientTests ) . Assembly . Location , $ "../{ cliName } ") ) ;
247+
248+ using var bicep = await clientFactory . Initialize (
249+ new ( ) { ExistingCliPath = cliPath , ConnectionMode = BicepConnectionMode . Stdio } ,
250+ TestContext . CancellationTokenSource . Token ) ;
251+
252+ var bicepFile = FileHelper . SaveResultFile ( TestContext , "main.bicep" , """
253+ param location string
254+ """ ) ;
255+
256+ var result = await bicep . Compile ( new ( bicepFile ) ) ;
257+
258+ result . Success . Should ( ) . BeTrue ( ) ;
259+ result . Contents . Should ( ) . NotBeNullOrEmpty ( ) ;
260+ result . Diagnostics . Should ( ) . Contain ( x => x . Code == "no-unused-params" ) ;
261+ }
262+
141263 [ TestMethod ]
142264 public async Task CompileParams_runs_successfully ( )
143265 {
0 commit comments