Skip to content

Commit aa5785f

Browse files
committed
profile reader tests
1 parent bc84d3c commit aa5785f

1 file changed

Lines changed: 281 additions & 0 deletions

File tree

profileutil/profile_reader_test.go

Lines changed: 281 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,281 @@
1+
package profileutil
2+
3+
import (
4+
"errors"
5+
"os"
6+
"path/filepath"
7+
"testing"
8+
9+
"github.com/bitrise-io/go-utils/v2/log"
10+
"github.com/bitrise-io/go-xcode/v2/mocks"
11+
"github.com/fullsailor/pkcs7"
12+
"github.com/stretchr/testify/require"
13+
)
14+
15+
func TestProfileReader_ProvisioningProfileInfoFromFile(t *testing.T) {
16+
t.Run("file open error is propagated", func(t *testing.T) {
17+
fileManager := mocks.NewFileManager(t)
18+
fileManager.On("Open", "/path/to/profile.mobileprovision").Return(nil, errors.New("file not found"))
19+
20+
reader := NewProfileReader(log.NewLogger(), fileManager, mocks.NewPathModifier(t), mocks.NewPathProvider(t))
21+
_, err := reader.ProvisioningProfileInfoFromFile("/path/to/profile.mobileprovision")
22+
23+
require.Error(t, err)
24+
})
25+
26+
t.Run("parses iOS profile from file", func(t *testing.T) {
27+
f := newPKCS7TempFile(t, iosDevelopmentProfileContent)
28+
fileManager := mocks.NewFileManager(t)
29+
fileManager.On("Open", f.Name()).Return(f, nil)
30+
31+
reader := NewProfileReader(log.NewLogger(), fileManager, mocks.NewPathModifier(t), mocks.NewPathProvider(t))
32+
got, err := reader.ProvisioningProfileInfoFromFile(f.Name())
33+
34+
require.NoError(t, err)
35+
require.Equal(t, ProfileTypeIos, got.Type)
36+
require.Equal(t, "4b617a5f-e31e-4edc-9460-718a5abacd05", got.UUID)
37+
})
38+
}
39+
40+
func TestProfileReader_InstalledProvisioningProfileInfos(t *testing.T) {
41+
const (
42+
modernTilde = "~/Library/Developer/Xcode/UserData/Provisioning Profiles"
43+
legacyTilde = "~/Library/MobileDevice/Provisioning Profiles"
44+
modernAbs = "/Users/user/Library/Developer/Xcode/UserData/Provisioning Profiles"
45+
legacyAbs = "/Users/user/Library/MobileDevice/Provisioning Profiles"
46+
)
47+
48+
t.Run("list profiles error is propagated", func(t *testing.T) {
49+
pathModifier := mocks.NewPathModifier(t)
50+
pathModifier.On("AbsPath", modernTilde).Return("", errors.New("access denied"))
51+
52+
reader := NewProfileReader(log.NewLogger(), mocks.NewFileManager(t), pathModifier, mocks.NewPathProvider(t))
53+
_, err := reader.InstalledProvisioningProfileInfos(ProfileTypeIos)
54+
55+
require.Error(t, err)
56+
})
57+
58+
t.Run("file open error is propagated", func(t *testing.T) {
59+
const profilePath = "/Users/user/Library/Developer/Xcode/UserData/Provisioning Profiles/uuid.mobileprovision"
60+
pathModifier := mocks.NewPathModifier(t)
61+
pathModifier.On("AbsPath", modernTilde).Return(modernAbs, nil)
62+
pathModifier.On("AbsPath", legacyTilde).Return(legacyAbs, nil)
63+
pathModifier.On("EscapeGlobPath", modernAbs).Return(modernAbs)
64+
pathModifier.On("EscapeGlobPath", legacyAbs).Return(legacyAbs)
65+
pathProvider := mocks.NewPathProvider(t)
66+
pathProvider.On("Glob", filepath.Join(modernAbs, "*"+IOSExtension)).Return([]string{profilePath}, nil)
67+
pathProvider.On("Glob", filepath.Join(legacyAbs, "*"+IOSExtension)).Return([]string{}, nil)
68+
fileManager := mocks.NewFileManager(t)
69+
fileManager.On("Open", profilePath).Return(nil, errors.New("permission denied"))
70+
71+
reader := NewProfileReader(log.NewLogger(), fileManager, pathModifier, pathProvider)
72+
_, err := reader.InstalledProvisioningProfileInfos(ProfileTypeIos)
73+
74+
require.Error(t, err)
75+
})
76+
77+
t.Run("returns parsed profiles", func(t *testing.T) {
78+
f := newPKCS7TempFile(t, iosDevelopmentProfileContent)
79+
pathModifier := mocks.NewPathModifier(t)
80+
pathModifier.On("AbsPath", modernTilde).Return(modernAbs, nil)
81+
pathModifier.On("AbsPath", legacyTilde).Return(legacyAbs, nil)
82+
pathModifier.On("EscapeGlobPath", modernAbs).Return(modernAbs)
83+
pathModifier.On("EscapeGlobPath", legacyAbs).Return(legacyAbs)
84+
pathProvider := mocks.NewPathProvider(t)
85+
pathProvider.On("Glob", filepath.Join(modernAbs, "*"+IOSExtension)).Return([]string{f.Name()}, nil)
86+
pathProvider.On("Glob", filepath.Join(legacyAbs, "*"+IOSExtension)).Return([]string{}, nil)
87+
fileManager := mocks.NewFileManager(t)
88+
fileManager.On("Open", f.Name()).Return(f, nil)
89+
90+
reader := NewProfileReader(log.NewLogger(), fileManager, pathModifier, pathProvider)
91+
got, err := reader.InstalledProvisioningProfileInfos(ProfileTypeIos)
92+
93+
require.NoError(t, err)
94+
require.Len(t, got, 1)
95+
require.Equal(t, ProfileTypeIos, got[0].Type)
96+
require.Equal(t, "4b617a5f-e31e-4edc-9460-718a5abacd05", got[0].UUID)
97+
})
98+
}
99+
100+
func TestProfileReader_ListProfiles(t *testing.T) {
101+
const (
102+
modernTilde = "~/Library/Developer/Xcode/UserData/Provisioning Profiles"
103+
legacyTilde = "~/Library/MobileDevice/Provisioning Profiles"
104+
modernAbs = "/Users/user/Library/Developer/Xcode/UserData/Provisioning Profiles"
105+
legacyAbs = "/Users/user/Library/MobileDevice/Provisioning Profiles"
106+
)
107+
108+
setupPathMocks := func(pathModifier *mocks.PathModifier, pathProvider *mocks.PathProvider, uuid, ext string, modernResults, legacyResults []string) {
109+
pathModifier.On("AbsPath", modernTilde).Return(modernAbs, nil)
110+
pathModifier.On("AbsPath", legacyTilde).Return(legacyAbs, nil)
111+
pathModifier.On("EscapeGlobPath", modernAbs).Return(modernAbs)
112+
pathModifier.On("EscapeGlobPath", legacyAbs).Return(legacyAbs)
113+
pathProvider.On("Glob", filepath.Join(modernAbs, uuid+ext)).Return(modernResults, nil)
114+
pathProvider.On("Glob", filepath.Join(legacyAbs, uuid+ext)).Return(legacyResults, nil)
115+
}
116+
117+
t.Run("iOS type uses .mobileprovision extension", func(t *testing.T) {
118+
uuid := "abc123"
119+
pathModifier := mocks.NewPathModifier(t)
120+
pathProvider := mocks.NewPathProvider(t)
121+
modernResult := []string{filepath.Join(modernAbs, uuid+IOSExtension)}
122+
setupPathMocks(pathModifier, pathProvider, uuid, IOSExtension, modernResult, []string{})
123+
124+
reader := newTestProfileReader(t, pathModifier, pathProvider)
125+
got, err := reader.ListProfiles(ProfileTypeIos, uuid)
126+
127+
require.NoError(t, err)
128+
require.Equal(t, modernResult, got)
129+
})
130+
131+
t.Run("macOS type uses .provisionprofile extension", func(t *testing.T) {
132+
uuid := "abc123"
133+
pathModifier := mocks.NewPathModifier(t)
134+
pathProvider := mocks.NewPathProvider(t)
135+
modernResult := []string{filepath.Join(modernAbs, uuid+MacExtension)}
136+
setupPathMocks(pathModifier, pathProvider, uuid, MacExtension, modernResult, []string{})
137+
138+
reader := newTestProfileReader(t, pathModifier, pathProvider)
139+
got, err := reader.ListProfiles(ProfileTypeMacOs, uuid)
140+
141+
require.NoError(t, err)
142+
require.Equal(t, modernResult, got)
143+
})
144+
145+
t.Run("results from both dirs are concatenated", func(t *testing.T) {
146+
uuid := "*"
147+
pathModifier := mocks.NewPathModifier(t)
148+
pathProvider := mocks.NewPathProvider(t)
149+
modernResults := []string{filepath.Join(modernAbs, "profile1.mobileprovision")}
150+
legacyResults := []string{filepath.Join(legacyAbs, "profile2.mobileprovision")}
151+
setupPathMocks(pathModifier, pathProvider, uuid, IOSExtension, modernResults, legacyResults)
152+
153+
reader := newTestProfileReader(t, pathModifier, pathProvider)
154+
got, err := reader.ListProfiles(ProfileTypeIos, uuid)
155+
156+
require.NoError(t, err)
157+
require.Equal(t, append(modernResults, legacyResults...), got)
158+
})
159+
160+
t.Run("AbsPath error is propagated", func(t *testing.T) {
161+
pathModifier := mocks.NewPathModifier(t)
162+
pathModifier.On("AbsPath", modernTilde).Return("", errors.New("access denied"))
163+
164+
reader := newTestProfileReader(t, pathModifier, mocks.NewPathProvider(t))
165+
_, err := reader.ListProfiles(ProfileTypeIos, "uuid")
166+
167+
require.Error(t, err)
168+
})
169+
170+
t.Run("Glob error is propagated", func(t *testing.T) {
171+
pathModifier := mocks.NewPathModifier(t)
172+
pathModifier.On("AbsPath", modernTilde).Return(modernAbs, nil)
173+
pathModifier.On("AbsPath", legacyTilde).Return(legacyAbs, nil)
174+
pathModifier.On("EscapeGlobPath", modernAbs).Return(modernAbs)
175+
pathProvider := mocks.NewPathProvider(t)
176+
pathProvider.On("Glob", filepath.Join(modernAbs, "*"+IOSExtension)).Return(nil, errors.New("glob failed"))
177+
178+
reader := newTestProfileReader(t, pathModifier, pathProvider)
179+
_, err := reader.ListProfiles(ProfileTypeIos, "*")
180+
181+
require.Error(t, err)
182+
})
183+
}
184+
185+
func TestProfileReader_ProvisioningProfilesDirPath(t *testing.T) {
186+
const (
187+
modernTilde = "~/Library/Developer/Xcode/UserData/Provisioning Profiles"
188+
legacyTilde = "~/Library/MobileDevice/Provisioning Profiles"
189+
modernAbs = "/Users/user/Library/Developer/Xcode/UserData/Provisioning Profiles"
190+
legacyAbs = "/Users/user/Library/MobileDevice/Provisioning Profiles"
191+
)
192+
193+
tests := []struct {
194+
name string
195+
xcodeMajorVersion int64
196+
expectedAbsPathArg string
197+
returnPath string
198+
}{
199+
{
200+
name: "xcode 0 (unknown) uses modern path",
201+
xcodeMajorVersion: 0,
202+
expectedAbsPathArg: modernTilde,
203+
returnPath: modernAbs,
204+
},
205+
{
206+
name: "xcode 16 uses modern path",
207+
xcodeMajorVersion: 16,
208+
expectedAbsPathArg: modernTilde,
209+
returnPath: modernAbs,
210+
},
211+
{
212+
name: "xcode 17 uses modern path",
213+
xcodeMajorVersion: 17,
214+
expectedAbsPathArg: modernTilde,
215+
returnPath: modernAbs,
216+
},
217+
{
218+
name: "xcode 15 uses legacy path",
219+
xcodeMajorVersion: 15,
220+
expectedAbsPathArg: legacyTilde,
221+
returnPath: legacyAbs,
222+
},
223+
{
224+
name: "xcode 1 uses legacy path",
225+
xcodeMajorVersion: 1,
226+
expectedAbsPathArg: legacyTilde,
227+
returnPath: legacyAbs,
228+
},
229+
{
230+
name: "modern path resolves tilde to $HOME",
231+
xcodeMajorVersion: 16,
232+
expectedAbsPathArg: modernTilde,
233+
returnPath: filepath.Join(os.Getenv("HOME"), "Library/Developer/Xcode/UserData/Provisioning Profiles"),
234+
},
235+
}
236+
237+
for _, tt := range tests {
238+
t.Run(tt.name, func(t *testing.T) {
239+
pathModifier := mocks.NewPathModifier(t)
240+
pathModifier.On("AbsPath", tt.expectedAbsPathArg).Return(tt.returnPath, nil)
241+
242+
reader := newTestProfileReader(t, pathModifier, mocks.NewPathProvider(t))
243+
got, err := reader.ProvisioningProfilesDirPath(tt.xcodeMajorVersion)
244+
245+
require.NoError(t, err)
246+
require.Equal(t, tt.returnPath, got)
247+
})
248+
}
249+
250+
t.Run("AbsPath error is propagated", func(t *testing.T) {
251+
pathModifier := mocks.NewPathModifier(t)
252+
pathModifier.On("AbsPath", modernTilde).Return("", errors.New("access denied"))
253+
254+
reader := newTestProfileReader(t, pathModifier, mocks.NewPathProvider(t))
255+
_, err := reader.ProvisioningProfilesDirPath(16)
256+
257+
require.Error(t, err)
258+
})
259+
}
260+
261+
func newTestProfileReader(t *testing.T, pathModifier *mocks.PathModifier, pathProvider *mocks.PathProvider) ProfileReader {
262+
return NewProfileReader(log.NewLogger(), mocks.NewFileManager(t), pathModifier, pathProvider)
263+
}
264+
265+
// newPKCS7TempFile writes the given plist content into a PKCS7 envelope, saves it to a
266+
// temp file, and returns the open file rewound to the beginning.
267+
func newPKCS7TempFile(t *testing.T, content string) *os.File {
268+
t.Helper()
269+
sd, err := pkcs7.NewSignedData([]byte(content))
270+
require.NoError(t, err)
271+
pkcs7Bytes, err := sd.Finish()
272+
require.NoError(t, err)
273+
274+
f, err := os.CreateTemp(t.TempDir(), "*.mobileprovision")
275+
require.NoError(t, err)
276+
_, err = f.Write(pkcs7Bytes)
277+
require.NoError(t, err)
278+
_, err = f.Seek(0, 0)
279+
require.NoError(t, err)
280+
return f
281+
}

0 commit comments

Comments
 (0)