1
+ import assert from 'assert' ;
2
+ import sinon from 'sinon' ;
3
+ import auth from '../../../../Auth.js' ;
4
+ import { cli } from '../../../../cli/cli.js' ;
5
+ import { CommandInfo } from '../../../../cli/CommandInfo.js' ;
6
+ import { Logger } from '../../../../cli/Logger.js' ;
7
+ import { CommandError } from '../../../../Command.js' ;
8
+ import request from '../../../../request.js' ;
9
+ import { telemetry } from '../../../../telemetry.js' ;
10
+ import { pid } from '../../../../utils/pid.js' ;
11
+ import { session } from '../../../../utils/session.js' ;
12
+ import { sinonUtil } from '../../../../utils/sinonUtil.js' ;
13
+ import commands from '../../commands.js' ;
14
+ import command from './model-get.js' ;
15
+
16
+ describe ( commands . MODEL_GET , ( ) => {
17
+ let log : string [ ] ;
18
+ let logger : Logger ;
19
+ let loggerLogSpy : sinon . SinonSpy ;
20
+ let commandInfo : CommandInfo ;
21
+ const model = {
22
+ "AIBuilderHybridModelType" : null ,
23
+ "AzureCognitivePrebuiltModelName" : null ,
24
+ "BaseContentTypeName" : null ,
25
+ "ConfidenceScore" : "{\"trainingStatus\":{\"kind\":\"original\",\"ClassifierStatus\":{\"TrainingStatus\":\"success\",\"TimeStamp\":1716547860981},\"ExtractorsStatus\":[{\"TimeStamp\":1716547860173,\"ExtractorName\":\"Name\",\"TrainingStatus\":\"success\"}]},\"modelAccuracy\":{\"Classifier\":1,\"Extractors\":{\"Name\":0.333333343}},\"perSampleAccuracy\":{\"1\":{\"Classifier\":1,\"Extractors\":{\"Name\":1}},\"2\":{\"Classifier\":1,\"Extractors\":{\"Name\":0}},\"3\":{\"Classifier\":1,\"Extractors\":{\"Name\":0}},\"4\":{\"Classifier\":1,\"Extractors\":{\"Name\":0}},\"5\":{\"Classifier\":1,\"Extractors\":{\"Name\":0}},\"6\":{\"Classifier\":1,\"Extractors\":{\"Name\":1}}},\"perSamplePrediction\":{\"1\":{\"Extractors\":{\"Name\":[\"Michał\"]}},\"2\":{\"Extractors\":{\"Name\":[]}},\"3\":{\"Extractors\":{\"Name\":[]}},\"4\":{\"Extractors\":{\"Name\":[]}},\"5\":{\"Extractors\":{\"Name\":[]}},\"6\":{\"Extractors\":{\"Name\":[]}}},\"trainingFailures\":{}}" ,
26
+ "ContentTypeGroup" : "Intelligent Document Content Types" ,
27
+ "ContentTypeId" : "0x010100A5C3671D1FB1A64D9F280C628D041692" ,
28
+ "ContentTypeName" : "TeachingModel" ,
29
+ "Created" : "2024-05-23T16:51:29Z" ,
30
+ "CreatedBy" : "i:0#.f|membership|user@contoso.onmicrosoft.com" ,
31
+ "DriveId" : "b!qTVLltt1P02LUgejOy6O_1amoFeu1EJBlawH83UtYbQs_H3KVKAcQpuQOpNLl646" ,
32
+ "Explanations" : "{\"Classifier\":[{\"id\":\"950f39cd-5e72-4442-ae8a-ea4bae32962c\",\"kind\":\"regexFeature\",\"name\":\"Email address\",\"active\":true,\"pattern\":\"[A-Za-z0-9._%-]+@[A-Za-z0-9.-]+.[A-Za-z]{2,6}\"},{\"id\":\"d3f2940d-1df1-4ba8-975a-db3a4d626d5c\",\"kind\":\"dictionaryFeature\",\"name\":\"FirstName\",\"active\":true,\"nGrams\":[\"Michał\"],\"caseSensitive\":false,\"ignoreDigitIdentity\":false,\"ignoreLetterIdentity\":false},{\"id\":\"077966e1-73be-44c9-855f-a4eade6a280b\",\"kind\":\"modelFeature\",\"name\":\"Name\",\"active\":true,\"modelReference\":\"Name\",\"conceptId\":\"309b64e5-acd5-4538-a5b6-c6bfcdc1ffbf\"}],\"Extractors\":{\"Name\":[{\"id\":\"69e412bc-e5bc-4657-b378-34a01966bb92\",\"kind\":\"dictionaryFeature\",\"name\":\"Before label\",\"active\":true,\"nGrams\":[\"Test\",\"'Surname\",\"Test (\"],\"caseSensitive\":false,\"ignoreDigitIdentity\":false,\"ignoreLetterIdentity\":false}]}}" ,
33
+ "ID" : 1 ,
34
+ "LastTrained" : "2024-05-24T17:51:16Z" ,
35
+ "ListID" : "ca7dfc2c-a054-421c-9b90-3a934b97ae3a" ,
36
+ "ModelSettings" : "{\"ModelOrigin\":{\"LibraryId\":\"8a6027ab-c584-4394-ba9c-3dc4dd152b65\",\"Published\":false,\"SelecteFileUniqueIds\":[]}}" ,
37
+ "ModelType" : 2 ,
38
+ "Modified" : "2024-05-24T17:51:02Z" ,
39
+ "ModifiedBy" : "i:0#.f|membership|user@contoso.onmicrosoft.com" ,
40
+ "ObjectId" : "01HQDCWVGIEBDRN3RVK5A3UJW3M4TCMT45" ,
41
+ "PublicationType" : 0 ,
42
+ "Schemas" : "{\"Version\":2,\"Extractors\":{\"Name\":{\"concepts\":{\"309b64e5-acd5-4538-a5b6-c6bfcdc1ffbf\":{\"name\":\"Name\"}},\"relationships\":[],\"id\":\"Name\"}}}" ,
43
+ "SourceSiteUrl" : "https://contoso.sharepoint.com/sites/SyntexTest" ,
44
+ "SourceUrl" : null ,
45
+ "SourceWebServerRelativeUrl" : "/sites/SyntexTest" ,
46
+ "UniqueId" : "164720c8-35ee-4157-ba26-db6726264f9d"
47
+ } ;
48
+
49
+ const publications = [
50
+ {
51
+ "Created" : "2020-11-03T12:52:12Z" ,
52
+ "CreatedBy" : "i:0#.f|membership|user@contoso.onmicrosoft.com" ,
53
+ "DriveId" : "b!7w273MPU8kiqSKc6SWaUkQ3wyRgeHbxDh-e6ShP9X-e-6B-bO36LRI87VfSrxwy9" ,
54
+ "HasTargetSitePermission" : true ,
55
+ "ID" : 1 ,
56
+ "ModelId" : 3 ,
57
+ "ModelName" : "BenefitsChangeNotice.classifier" ,
58
+ "ModelType" : 0 ,
59
+ "ModelUniqueId" : "b10e0de5-c069-46f9-90f7-4fb8ac001372" ,
60
+ "ModelVersion" : "2.0" ,
61
+ "Modified" : "2020-11-03T18:45:18Z" ,
62
+ "ModifiedBy" : "i:0#.f|membership|user@contoso.onmicrosoft.com" ,
63
+ "ObjectId" : "01XHOXSCVXVZCQAAQWCNGK6NGADKKZUMW7" ,
64
+ "PublicationType" : 1 ,
65
+ "TargetLibraryId" : "7eb6c306-1680-4ba9-9bbe-6b5b7efb27be" ,
66
+ "TargetLibraryName" : "Documents" ,
67
+ "TargetLibraryRemoved" : false ,
68
+ "TargetLibraryServerRelativeUrl" : "/sites/SyntexTest/Shared%20Documents" ,
69
+ "TargetLibraryUrl" : "https://contoso.sharepoint.com/sites/SyntexTest/Shared%20Documents" ,
70
+ "TargetSiteId" : "7e44de0a-30e3-45ab-a601-057298c9068f" ,
71
+ "TargetSiteUrl" : "https://contoso.sharepoint.com/sites/SyntexTest" ,
72
+ "TargetTableListId" : "00000000-0000-0000-0000-000000000000" ,
73
+ "TargetTableListName" : null ,
74
+ "TargetTableListRemoved" : false ,
75
+ "TargetTableListServerRelativeUrl" : null ,
76
+ "TargetTableListUrl" : null ,
77
+ "TargetWebId" : "9166df43-8e45-43b8-94ef-3a22826346de" ,
78
+ "TargetWebName" : "SyntexTest" ,
79
+ "TargetWebServerRelativeUrl" : "/sites/SyntexTest" ,
80
+ "UniqueId" : "0045aeb7-1602-4c13-af34-c01a959a32df" ,
81
+ "ViewOption" : "NewViewAsDefault"
82
+ }
83
+ ] ;
84
+
85
+ before ( ( ) => {
86
+ sinon . stub ( auth , 'restoreAuth' ) . resolves ( ) ;
87
+ sinon . stub ( telemetry , 'trackEvent' ) . returns ( ) ;
88
+ sinon . stub ( pid , 'getProcessName' ) . returns ( '' ) ;
89
+ sinon . stub ( session , 'getId' ) . returns ( '' ) ;
90
+ auth . connection . active = true ;
91
+ commandInfo = cli . getCommandInfo ( command ) ;
92
+ } ) ;
93
+
94
+ beforeEach ( ( ) => {
95
+ log = [ ] ;
96
+ logger = {
97
+ log : async ( msg : string ) => {
98
+ log . push ( msg ) ;
99
+ } ,
100
+ logRaw : async ( msg : string ) => {
101
+ log . push ( msg ) ;
102
+ } ,
103
+ logToStderr : async ( msg : string ) => {
104
+ log . push ( msg ) ;
105
+ }
106
+ } ;
107
+ loggerLogSpy = sinon . spy ( logger , 'log' ) ;
108
+ } ) ;
109
+
110
+ afterEach ( ( ) => {
111
+ sinonUtil . restore ( [
112
+ request . get
113
+ ] ) ;
114
+ } ) ;
115
+
116
+ after ( ( ) => {
117
+ sinon . restore ( ) ;
118
+ auth . connection . active = false ;
119
+ } ) ;
120
+
121
+ it ( 'has correct name' , ( ) => {
122
+ assert . strictEqual ( command . name , commands . MODEL_GET ) ;
123
+ } ) ;
124
+
125
+ it ( 'has a description' , ( ) => {
126
+ assert . notStrictEqual ( command . description , null ) ;
127
+ } ) ;
128
+
129
+ it ( 'passes validation when required parameters are valid with id' , async ( ) => {
130
+ const actual = await command . validate ( { options : { siteUrl : 'https://contoso.sharepoint.com/sites/sales' , id : '9b1b1e42-794b-4c71-93ac-5ed92488b67f' } } , commandInfo ) ;
131
+ assert . strictEqual ( actual , true ) ;
132
+ } ) ;
133
+
134
+ it ( 'passes validation when required parameters are valid with title' , async ( ) => {
135
+ const actual = await command . validate ( { options : { siteUrl : 'https://contoso.sharepoint.com/sites/sales' , title : 'ModelName' } } , commandInfo ) ;
136
+ assert . strictEqual ( actual , true ) ;
137
+ } ) ;
138
+
139
+ it ( 'passes validation when required parameters are valid with id and listPublications' , async ( ) => {
140
+ const actual = await command . validate ( { options : { siteUrl : 'https://contoso.sharepoint.com/sites/sales' , id : '9b1b1e42-794b-4c71-93ac-5ed92488b67f' , listPublications : true } } , commandInfo ) ;
141
+ assert . strictEqual ( actual , true ) ;
142
+ } ) ;
143
+
144
+ it ( 'passes validation when required parameters are valid with title and listPublications' , async ( ) => {
145
+ const actual = await command . validate ( { options : { siteUrl : 'https://contoso.sharepoint.com/sites/sales' , title : 'ModelName' , listPublications : true } } , commandInfo ) ;
146
+ assert . strictEqual ( actual , true ) ;
147
+ } ) ;
148
+
149
+ it ( 'fails validation when siteUrl is not valid' , async ( ) => {
150
+ const actual = await command . validate ( { options : { siteUrl : 'invalidUrl' , id : '9b1b1e42-794b-4c71-93ac-5ed92488b67f' } } , commandInfo ) ;
151
+ assert . notStrictEqual ( actual , true ) ;
152
+ } ) ;
153
+
154
+ it ( 'fails validation when id is not valid' , async ( ) => {
155
+ const actual = await command . validate ( { options : { siteUrl : 'https://contoso.sharepoint.com/sites/sales' , id : 'foo' } } , commandInfo ) ;
156
+ assert . notStrictEqual ( actual , true ) ;
157
+ } ) ;
158
+
159
+ it ( 'correctly handles site is not Content Site' , async ( ) => {
160
+ sinon . stub ( request , 'get' ) . callsFake ( async ( opts ) => {
161
+ if ( opts . url === `https://contoso.sharepoint.com/sites/portal/_api/web?$select=WebTemplateConfiguration` ) {
162
+ return {
163
+ WebTemplateConfiguration : 'SITEPAGEPUBLISHING#0'
164
+ } ;
165
+ }
166
+
167
+ throw 'Invalid request' ;
168
+ } ) ;
169
+
170
+ await assert . rejects ( command . action ( logger , { options : { verbose : true , siteUrl : 'https://contoso.sharepoint.com/sites/portal' , id : '9b1b1e42-794b-4c71-93ac-5ed92488b67f' } } ) ,
171
+ new CommandError ( 'https://contoso.sharepoint.com/sites/portal is not a content site.' ) ) ;
172
+ } ) ;
173
+
174
+
175
+ it ( 'correctly handles an access denied error' , async ( ) => {
176
+ sinon . stub ( request , 'get' ) . callsFake ( async ( opts ) => {
177
+ if ( opts . url === `https://contoso.sharepoint.com/sites/portal/_api/web?$select=WebTemplateConfiguration` ) {
178
+ throw {
179
+ error : {
180
+ "odata.error" : {
181
+ message : {
182
+ lang : "en-US" ,
183
+ value : "Attempted to perform an unauthorized operation."
184
+ }
185
+ }
186
+ }
187
+ } ;
188
+ }
189
+
190
+ throw 'Invalid request' ;
191
+ } ) ;
192
+
193
+ await assert . rejects ( command . action ( logger , { options : { verbose : true , siteUrl : 'https://contoso.sharepoint.com/sites/portal' , id : '9b1b1e42-794b-4c71-93ac-5ed92488b67f' } } ) ,
194
+ new CommandError ( 'Attempted to perform an unauthorized operation.' ) ) ;
195
+ } ) ;
196
+
197
+
198
+ it ( 'retrieves model by id' , async ( ) => {
199
+ sinon . stub ( request , 'get' ) . callsFake ( async ( opts ) => {
200
+ if ( opts . url === `https://contoso.sharepoint.com/sites/portal/_api/web?$select=WebTemplateConfiguration` ) {
201
+ return {
202
+ WebTemplateConfiguration : 'CONTENTCTR#0'
203
+ } ;
204
+ }
205
+
206
+ if ( opts . url === `https://contoso.sharepoint.com/sites/portal/_api/machinelearning/models/getbyuniqueid('9b1b1e42-794b-4c71-93ac-5ed92488b67f')` ) {
207
+ return model ;
208
+ }
209
+
210
+ throw 'Invalid request' ;
211
+ } ) ;
212
+
213
+ await command . action ( logger , { options : { siteUrl : 'https://contoso.sharepoint.com/sites/portal' , id : '9b1b1e42-794b-4c71-93ac-5ed92488b67f' } } ) ;
214
+ assert ( loggerLogSpy . calledOnceWithExactly ( model ) ) ;
215
+ } ) ;
216
+
217
+ it ( 'retrieves model by id with listPublications' , async ( ) => {
218
+ sinon . stub ( request , 'get' ) . callsFake ( async ( opts ) => {
219
+ if ( opts . url === `https://contoso.sharepoint.com/sites/portal/_api/web?$select=WebTemplateConfiguration` ) {
220
+ return {
221
+ WebTemplateConfiguration : 'CONTENTCTR#0'
222
+ } ;
223
+ }
224
+
225
+ if ( opts . url === `https://contoso.sharepoint.com/sites/portal/_api/machinelearning/models/getbyuniqueid('164720c8-35ee-4157-ba26-db6726264f9d')` ) {
226
+ return model ;
227
+ }
228
+
229
+ if ( opts . url === `https://contoso.sharepoint.com/sites/portal/_api/machinelearning/publications/getbymodeluniqueid('164720c8-35ee-4157-ba26-db6726264f9d')` ) {
230
+ return { value : publications } ;
231
+ }
232
+
233
+ throw 'Invalid request' ;
234
+ } ) ;
235
+
236
+ await command . action ( logger , { options : { siteUrl : 'https://contoso.sharepoint.com/sites/portal' , id : '164720c8-35ee-4157-ba26-db6726264f9d' , listPublications : true } } ) ;
237
+ assert ( loggerLogSpy . calledOnceWithExactly ( { ...model , Publications : publications } ) ) ;
238
+ } ) ;
239
+
240
+ it ( 'gets correct model when the site URL has a trailing slash' , async ( ) => {
241
+ sinon . stub ( request , 'get' ) . callsFake ( async ( opts ) => {
242
+ if ( opts . url === `https://contoso.sharepoint.com/sites/portal/_api/web?$select=WebTemplateConfiguration` ) {
243
+ return {
244
+ WebTemplateConfiguration : 'CONTENTCTR#0'
245
+ } ;
246
+ }
247
+
248
+ if ( opts . url === `https://contoso.sharepoint.com/sites/portal/_api/machinelearning/models/getbyuniqueid('9b1b1e42-794b-4c71-93ac-5ed92488b67f')` ) {
249
+ return model ;
250
+ }
251
+
252
+ throw 'Invalid request' ;
253
+ } ) ;
254
+
255
+ await command . action ( logger , { options : { siteUrl : 'https://contoso.sharepoint.com/sites/portal/' , id : '9b1b1e42-794b-4c71-93ac-5ed92488b67f' } } ) ;
256
+ assert ( loggerLogSpy . calledOnceWithExactly ( model ) ) ;
257
+ } ) ;
258
+
259
+ it ( 'retrieves model by title' , async ( ) => {
260
+ sinon . stub ( request , 'get' ) . callsFake ( async ( opts ) => {
261
+ if ( opts . url === `https://contoso.sharepoint.com/sites/portal/_api/web?$select=WebTemplateConfiguration` ) {
262
+ return {
263
+ WebTemplateConfiguration : 'CONTENTCTR#0'
264
+ } ;
265
+ }
266
+
267
+ if ( opts . url === `https://contoso.sharepoint.com/sites/portal/_api/machinelearning/models/getbytitle('ModelName')` ) {
268
+ return model ;
269
+ }
270
+
271
+ throw 'Invalid request' ;
272
+ } ) ;
273
+
274
+ await command . action ( logger , { options : { siteUrl : 'https://contoso.sharepoint.com/sites/portal' , title : 'ModelName' } } ) ;
275
+ assert ( loggerLogSpy . calledOnceWithExactly ( model ) ) ;
276
+ } ) ;
277
+
278
+ it ( 'retrieves model by title with listPublications' , async ( ) => {
279
+ sinon . stub ( request , 'get' ) . callsFake ( async ( opts ) => {
280
+ if ( opts . url === `https://contoso.sharepoint.com/sites/portal/_api/web?$select=WebTemplateConfiguration` ) {
281
+ return {
282
+ WebTemplateConfiguration : 'CONTENTCTR#0'
283
+ } ;
284
+ }
285
+
286
+ if ( opts . url === `https://contoso.sharepoint.com/sites/portal/_api/machinelearning/models/getbytitle('ModelName')` ) {
287
+ return model ;
288
+ }
289
+
290
+ if ( opts . url === `https://contoso.sharepoint.com/sites/portal/_api/machinelearning/publications/getbymodeluniqueid('164720c8-35ee-4157-ba26-db6726264f9d')` ) {
291
+ return { value : publications } ;
292
+ }
293
+
294
+ throw 'Invalid request' ;
295
+ } ) ;
296
+
297
+ await command . action ( logger , { options : { siteUrl : 'https://contoso.sharepoint.com/sites/portal' , title : 'ModelName' , listPublications : true } } ) ;
298
+ assert ( loggerLogSpy . calledOnceWithExactly ( { ...model , Publications : publications } ) ) ;
299
+ } ) ;
300
+ } ) ;
0 commit comments