55 "errors"
66 "fmt"
77 "os"
8+ "os/exec"
89 "path/filepath"
910 "runtime"
1011 "strings"
@@ -22,7 +23,7 @@ func TestSupportedVersion(t *testing.T) {
2223 "fastlane 1.300.0" : false ,
2324 "something else" : false ,
2425 } {
25- err := supportedVersion (output )
26+ err := supportedVersion (output , "brew upgrade fastlane" )
2627 if (err == nil ) != supported {
2728 t .Errorf ("supportedVersion(%q) = %v" , output , err )
2829 }
@@ -251,3 +252,180 @@ func TestCreateKeyWillNotBypassAPinnedFastlane(t *testing.T) {
251252 t .Fatalf ("err = %v" , err )
252253 }
253254}
255+
256+ // choosingAsker answers every choice with choice and records the questions.
257+ type choosingAsker struct {
258+ choice int
259+ asked []string
260+ }
261+
262+ func (c * choosingAsker ) Ask (question string , secret bool ) (string , error ) {
263+ return "" , errors .New ("not expected: " + question )
264+ }
265+
266+ func (c * choosingAsker ) Choose (question string , options []string ) (int , error ) {
267+ c .asked = append (c .asked , question )
268+
269+ return c .choice , nil
270+ }
271+
272+ // withoutFastlane leaves PATH with only bin, so fastlane is not found unless
273+ // something puts it there.
274+ func withoutFastlane (t * testing.T ) string {
275+ t .Helper ()
276+ if runtime .GOOS == "windows" {
277+ t .Skip ("shell stand-ins" )
278+ }
279+ bin := t .TempDir ()
280+ t .Setenv ("PATH" , bin )
281+
282+ return bin
283+ }
284+
285+ // fakeBrew writes a brew stand-in into bin that logs its arguments and, for
286+ // install fastlane, copies fastlane into bin.
287+ func fakeBrew (t * testing.T , bin , fastlane string ) string {
288+ t .Helper ()
289+ log := filepath .Join (t .TempDir (), "brew.log" )
290+ script := `#!/bin/sh
291+ echo "brew $*" >> "` + log + `"
292+ [ "$1 $2" = "install fastlane" ] && /bin/cp "` + fastlane + `" "` + bin + `/fastlane"
293+ `
294+ if err := os .WriteFile (filepath .Join (bin , "brew" ), []byte (script ), 0o700 ); err != nil {
295+ t .Fatal (err )
296+ }
297+
298+ return log
299+ }
300+
301+ func TestCreateKeyExplainsHowToInstallFastlane (t * testing.T ) {
302+ withoutFastlane (t )
303+ request := apns.Request {Name : "Appwrite Push" , Environment : apns .EnvironmentAll , Asker : & choosingAsker {}}
304+
305+ _ , err := (& Adapter {}).CreateKey (context .Background (), request )
306+ if ! errors .Is (err , apns .ErrUnavailable ) {
307+ t .Fatalf ("err = %v" , err )
308+ }
309+ for _ , part := range []string {"fastlane is not installed" , "brew install fastlane" , "gem install fastlane" , "--provider appwrite" } {
310+ if ! strings .Contains (err .Error (), part ) {
311+ t .Errorf ("message is missing %q: %v" , part , err )
312+ }
313+ }
314+ }
315+
316+ func TestCreateKeyInstallsFastlaneWithHomebrew (t * testing.T ) {
317+ fastlane , _ := fakeFastlane (t , "2.228.0" , `{"keyId":"KEY1234567","teamId":"ABCDE12345","p8":"pem"}` )
318+ bin := withoutFastlane (t )
319+ log := fakeBrew (t , bin , fastlane )
320+ asker := & choosingAsker {choice : 0 }
321+ var logged []string
322+ request := apns.Request {
323+ Name : "Appwrite Push" , Environment : apns .EnvironmentAll , AppleID : "dev@example.com" , Password : "secret" , Asker : asker ,
324+ Log : func (format string , args ... any ) { logged = append (logged , format ) },
325+ }
326+
327+ key , err := (& Adapter {Stdout : & strings.Builder {}, Stderr : & strings.Builder {}}).CreateKey (context .Background (), request )
328+ if err != nil || key .KeyID != "KEY1234567" {
329+ t .Fatalf ("key = %+v, %v" , key , err )
330+ }
331+ if strings .Join (asker .asked , "|" ) != "fastlane is not installed. Install it with Homebrew now?" {
332+ t .Errorf ("asked %v" , asker .asked )
333+ }
334+ if calls , _ := os .ReadFile (log ); strings .TrimSpace (string (calls )) != "brew install fastlane" {
335+ t .Errorf ("brew calls = %q" , calls )
336+ }
337+ if len (logged ) != 1 || logged [0 ] != "Running brew install fastlane ..." {
338+ t .Errorf ("logged %v" , logged )
339+ }
340+ }
341+
342+ func TestCreateKeyDoesNotInstallFastlaneWhenDeclined (t * testing.T ) {
343+ fastlane , _ := fakeFastlane (t , "2.228.0" , `{}` )
344+ bin := withoutFastlane (t )
345+ log := fakeBrew (t , bin , fastlane )
346+
347+ _ , err := (& Adapter {}).CreateKey (context .Background (), apns.Request {Name : "Appwrite Push" , Environment : apns .EnvironmentAll , Asker : & choosingAsker {choice : 1 }})
348+ if ! errors .Is (err , apns .ErrUnavailable ) || ! strings .Contains (err .Error (), "brew install fastlane" ) {
349+ t .Fatalf ("err = %v" , err )
350+ }
351+ if _ , err := os .Stat (log ); err == nil {
352+ t .Error ("ran brew after the offer was declined" )
353+ }
354+ }
355+
356+ func TestOldFastlaneSaysHowToUpgrade (t * testing.T ) {
357+ if err := supportedVersion ("fastlane 2.220.0" , "cd /app && bundle update fastlane" ); err == nil ||
358+ ! strings .Contains (err .Error (), "found 2.220.0. Upgrade it with cd /app && bundle update fastlane" ) {
359+ t .Errorf ("err = %v" , err )
360+ }
361+ }
362+
363+ func TestAConfiguredFastlaneIsNeverInstalled (t * testing.T ) {
364+ fastlane , _ := fakeFastlane (t , "2.228.0" , `{}` )
365+ bin := withoutFastlane (t )
366+ log := fakeBrew (t , bin , fastlane )
367+ asker := & choosingAsker {choice : 0 }
368+
369+ _ , err := (& Adapter {Fastlane : "fastlane" }).CreateKey (context .Background (), apns.Request {Name : "Appwrite Push" , Environment : apns .EnvironmentAll , Asker : asker })
370+ if ! errors .Is (err , apns .ErrUnavailable ) {
371+ t .Fatalf ("err = %v" , err )
372+ }
373+ if len (asker .asked ) != 0 {
374+ t .Errorf ("offered to install: %v" , asker .asked )
375+ }
376+ if _ , err := os .Stat (log ); err == nil {
377+ t .Error ("ran brew for a configured command" )
378+ }
379+ }
380+
381+ func TestTheBundlerUpgradeCommandRuns (t * testing.T ) {
382+ if runtime .GOOS == "windows" {
383+ t .Skip ("shell stand-in" )
384+ }
385+ project := t .TempDir ()
386+ gemfile := filepath .Join (project , "Gemfile" )
387+ if err := os .WriteFile (gemfile , []byte ("gem \" fastlane\" \n " ), 0o600 ); err != nil {
388+ t .Fatal (err )
389+ }
390+ bundle := filepath .Join (t .TempDir (), "bundle" )
391+ if err := os .WriteFile (bundle , []byte ("#!/bin/sh\n echo \" fastlane 2.220.0\" \n " ), 0o700 ); err != nil {
392+ t .Fatal (err )
393+ }
394+
395+ _ , err := (& Adapter {Bundle : bundle , WorkDir : project }).CreateKey (context .Background (), apns.Request {Name : "Appwrite Push" , Environment : apns .EnvironmentAll , AppleID : "dev@example.com" , Password : "secret" })
396+ if err == nil || ! strings .Contains (err .Error (), "Upgrade it with cd " + project + " && bundle update fastlane" ) {
397+ t .Errorf ("err = %v" , err )
398+ }
399+ }
400+
401+ func TestBundlerUpgrade (t * testing.T ) {
402+ for _ , test := range []struct { folder , goos , want string }{
403+ {"/Users/dev/app" , "darwin" , "cd /Users/dev/app && bundle update fastlane" },
404+ {"/Users/dev/My App" , "darwin" , "cd '/Users/dev/My App' && bundle update fastlane" },
405+ {"/Users/dev/it's" , "linux" , `cd '/Users/dev/it'\''s' && bundle update fastlane` },
406+ {`/Users/dev/back\slash` , "linux" , `cd '/Users/dev/back\slash' && bundle update fastlane` },
407+ {`/Users/dev/$(whoami)` , "linux" , `cd '/Users/dev/$(whoami)' && bundle update fastlane` },
408+ {`C:\Users\dev\$(Get-Date) App` , "windows" , `bundle update fastlane, run from the folder C:\Users\dev\$(Get-Date) App` },
409+ } {
410+ if got := bundlerUpgrade (test .folder , test .goos ); got != test .want {
411+ t .Errorf ("bundlerUpgrade(%q, %s) = %s, want %s" , test .folder , test .goos , got , test .want )
412+ }
413+ }
414+ }
415+
416+ // The quoted command must work when pasted into a shell, for a folder whose
417+ // name has spaces and a quote.
418+ func TestTheQuotedCdRunsInAShell (t * testing.T ) {
419+ if runtime .GOOS == "windows" {
420+ t .Skip ("POSIX shell" )
421+ }
422+ folder := filepath .Join (t .TempDir (), "My Project's App" )
423+ if err := os .Mkdir (folder , 0o700 ); err != nil {
424+ t .Fatal (err )
425+ }
426+ output , err := exec .Command ("/bin/sh" , "-c" , "cd " + shellQuote (folder )+ " && pwd -P" ).Output ()
427+ resolved , _ := filepath .EvalSymlinks (folder )
428+ if err != nil || strings .TrimSpace (string (output )) != resolved {
429+ t .Errorf ("cd landed in %q, %v; want %q" , output , err , resolved )
430+ }
431+ }
0 commit comments