@@ -25,126 +25,165 @@ public CopilotTests()
2525 _config = new TestsAppConfig ( ) ;
2626 }
2727
28- // https://learn.microsoft.com/en-us/office/office-365-management-api/copilot-schema
29- [ TestMethod ]
30- public async Task CopilotEventManagerSaveTest ( )
28+ // Shared flow for saving Copilot events (normal + no permissions adaptor)
29+ private async Task ExecuteCopilotEventManagerSaveFlow ( ICopilotMetadataLoader adaptor , AnalyticsEntitiesContext db )
3130 {
32- using ( var db = new AnalyticsEntitiesContext ( ) )
31+
32+ var copilotEventManager = new CopilotAuditEventManager ( _config . ConnectionStrings . DatabaseConnectionString , adaptor , _logger ) ;
33+
34+ var commonEventDocEdit = new Office365Event
3335 {
34- // Clear events for test
35- db . CopilotEventMetadataFiles . RemoveRange ( db . CopilotEventMetadataFiles ) ;
36- db . CopilotEventMetadataMeetings . RemoveRange ( db . CopilotEventMetadataMeetings ) ;
37- await db . SaveChangesAsync ( ) ;
36+ TimeStamp = DateTime . Now ,
37+ Operation = new EventOperation { Name = "Document Edit" + DateTime . Now . Ticks } ,
38+ User = new User { AzureAdId = "test" , UserPrincipalName = "test doc user " + DateTime . Now . Ticks } ,
39+ Id = Guid . NewGuid ( )
40+ } ;
41+ var commonEventChat = new Office365Event
42+ {
43+ TimeStamp = DateTime . Now ,
44+ Operation = new EventOperation { Name = "Chat or something" + DateTime . Now . Ticks } ,
45+ User = new User { AzureAdId = "test" , UserPrincipalName = "test chat user " + DateTime . Now . Ticks } ,
46+ Id = Guid . NewGuid ( )
47+ } ;
48+ var commonEventMeeting = new Office365Event
49+ {
50+ TimeStamp = DateTime . Now ,
51+ Operation = new EventOperation { Name = "Meeting Op" + DateTime . Now . Ticks } ,
52+ User = new User { AzureAdId = "test" , UserPrincipalName = "test meeting user " + DateTime . Now . Ticks } ,
53+ Id = Guid . NewGuid ( )
54+ } ;
55+ var commonOutlook = new Office365Event
56+ {
57+ TimeStamp = DateTime . Now ,
58+ Operation = new EventOperation { Name = "Outlook Op" + DateTime . Now . Ticks } ,
59+ User = new User { AzureAdId = "test" , UserPrincipalName = "test outlook user " + DateTime . Now . Ticks } ,
60+ Id = Guid . NewGuid ( )
61+ } ;
3862
39- var copilotEventAdaptor = new CopilotAuditEventManager ( _config . ConnectionStrings . DatabaseConnectionString , new FakeCopilotEventAdaptor ( ) , _logger ) ;
40-
41- var commonEventDocEdit = new Office365Event
42- {
43- TimeStamp = DateTime . Now ,
44- Operation = new EventOperation { Name = "Document Edit" + DateTime . Now . Ticks } ,
45- User = new User { AzureAdId = "test" , UserPrincipalName = "test doc user " + DateTime . Now . Ticks } ,
46- Id = Guid . NewGuid ( )
47- } ;
48- var commonEventChat = new Office365Event
49- {
50- TimeStamp = DateTime . Now ,
51- Operation = new EventOperation { Name = "Chat or something" + DateTime . Now . Ticks } ,
52- User = new User { AzureAdId = "test" , UserPrincipalName = "test chat user " + DateTime . Now . Ticks } ,
53- Id = Guid . NewGuid ( )
54- } ;
55- var commonEventMeeting = new Office365Event
56- {
57- TimeStamp = DateTime . Now ,
58- Operation = new EventOperation { Name = "Meeting Op" + DateTime . Now . Ticks } ,
59- User = new User { AzureAdId = "test" , UserPrincipalName = "test meeting user " + DateTime . Now . Ticks } ,
60- Id = Guid . NewGuid ( )
61- } ;
62- var commonOutlook = new Office365Event
63- {
64- TimeStamp = DateTime . Now ,
65- Operation = new EventOperation { Name = "Outlook Op" + DateTime . Now . Ticks } ,
66- User = new User { AzureAdId = "test" , UserPrincipalName = "test outlook user " + DateTime . Now . Ticks } ,
67- Id = Guid . NewGuid ( )
68- } ;
69-
70- // Audit metadata for our tests
71- var meeting = new CopilotEventData
72- {
73- AppHost = "test" ,
74- Contexts = new List < Context >
63+ // Audit metadata for tests
64+ var meeting = new CopilotEventData
65+ {
66+ AppHost = "test" ,
67+ Contexts = new List < Context >
7568 {
7669 new Context
7770 {
78- Id = "https://microsoft.teams.com/threads/19:meeting_NDQ4MGRhYjgtMzc5MS00ZWMxLWJiZjEtOTIxZmM5Mzg3ZGFi@thread.v2" , // Needs to be real
71+ Id = "https://microsoft.teams.com/threads/19:meeting_NDQ4MGRhYjgtMzc5MS00ZWMxLWJiZjEtOTIxZmM5Mzg3ZGFi@thread.v2" , // Needs to be real
7972 Type = ActivityImportConstants . COPILOT_CONTEXT_TYPE_TEAMS_MEETING
8073 }
8174 }
82- } ;
83- var docEvent = new CopilotEventData
84- {
85- AppHost = "Word" ,
86- Contexts = new List < Context >
75+ } ;
76+ var docEvent = new CopilotEventData
77+ {
78+ AppHost = "Word" ,
79+ Contexts = new List < Context >
8780 {
8881 new Context
8982 {
9083 Id = _config . TestCopilotDocContextIdSpSite ,
9184 Type = _config . TeamSiteFileExtension
9285 }
9386 }
94- } ;
95- var teamsChat = new CopilotEventData
96- {
97- AppHost = "Teams" ,
98- Contexts = new List < Context >
87+ } ;
88+ var teamsChat = new CopilotEventData
89+ {
90+ AppHost = "Teams" ,
91+ Contexts = new List < Context >
9992 {
10093 new Context
10194 {
10295 Id = "https://microsoft.teams.com/threads/19:somechatthread@thread.v2" ,
10396 Type = ActivityImportConstants . COPILOT_CONTEXT_TYPE_TEAMS_CHAT
10497 }
10598 }
106- } ;
107-
108- var outlook = new CopilotEventData
109- {
110- AppHost = "Outlook" ,
111- AccessedResources = new List < AccessedResource >
99+ } ;
100+ var outlook = new CopilotEventData
101+ {
102+ AppHost = "Outlook" ,
103+ AccessedResources = new List < AccessedResource >
112104 {
113105 new AccessedResource { Type = "http://schema.skype.com/HyperLink" }
114106 } ,
115- } ;
107+ } ;
108+
109+ // Persist common events for FK usage
110+ db . AuditEventsCommon . Add ( commonEventDocEdit ) ;
111+ db . AuditEventsCommon . Add ( commonEventMeeting ) ;
112+ db . AuditEventsCommon . Add ( commonEventChat ) ;
113+ db . AuditEventsCommon . Add ( commonOutlook ) ;
114+ await db . SaveChangesAsync ( ) ;
115+
116+ // Save Copilot events
117+ await copilotEventManager . SaveSingleCopilotEventToSql ( meeting , commonEventMeeting ) ;
118+ await copilotEventManager . SaveSingleCopilotEventToSql ( docEvent , commonEventDocEdit ) ;
119+ await copilotEventManager . SaveSingleCopilotEventToSql ( teamsChat , commonEventChat ) ;
120+ await copilotEventManager . SaveSingleCopilotEventToSql ( outlook , commonOutlook ) ;
121+ await copilotEventManager . CommitAllChanges ( ) ;
116122
123+ }
124+
125+ [ TestMethod ]
126+ public async Task CopilotEventManagerSaveTest ( )
127+ {
128+
129+ using ( var db = new AnalyticsEntitiesContext ( ) )
130+ {
131+ db . CopilotEventMetadataFiles . RemoveRange ( db . CopilotEventMetadataFiles ) ;
132+ db . CopilotEventMetadataMeetings . RemoveRange ( db . CopilotEventMetadataMeetings ) ;
133+ await db . SaveChangesAsync ( ) ;
117134
118- // Check counts before and after
135+ // Counts before
119136 var fileEventsPreCount = await db . CopilotEventMetadataFiles . CountAsync ( ) ;
120137 var meetingEventsPreCount = await db . CopilotEventMetadataMeetings . CountAsync ( ) ;
121138 var allCopilotEventsPreCount = await db . CopilotChats . CountAsync ( ) ;
122139
123- // Save common events as they are required for the foreign key - the common event is saved before CopilotAuditEventManager runs on the metadata
124- db . AuditEventsCommon . Add ( commonEventDocEdit ) ;
125- db . AuditEventsCommon . Add ( commonEventMeeting ) ;
126- db . AuditEventsCommon . Add ( commonEventChat ) ;
127- db . AuditEventsCommon . Add ( commonOutlook ) ;
128- await db . SaveChangesAsync ( ) ;
140+ await ExecuteCopilotEventManagerSaveFlow ( new FakeCopilotEventAdaptor ( ) , db ) ;
129141
130- // Save events
131- await copilotEventAdaptor . SaveSingleCopilotEventToSql ( meeting , commonEventMeeting ) ;
132- await copilotEventAdaptor . SaveSingleCopilotEventToSql ( docEvent , commonEventDocEdit ) ;
133- await copilotEventAdaptor . SaveSingleCopilotEventToSql ( teamsChat , commonEventChat ) ;
134- await copilotEventAdaptor . SaveSingleCopilotEventToSql ( outlook , commonOutlook ) ;
135- await copilotEventAdaptor . CommitAllChanges ( ) ;
136142
137- // Verify counts have increased
143+ // Counts after
138144 var fileEventsPostCount = await db . CopilotEventMetadataFiles . CountAsync ( ) ;
139145 var meetingEventsPostCount = await db . CopilotEventMetadataMeetings . CountAsync ( ) ;
140146 var allCopilotEventsPostCount = await db . CopilotChats . CountAsync ( ) ;
141147
148+ // Assertions
142149 Assert . IsTrue ( fileEventsPostCount == fileEventsPreCount + 1 ) ;
143150 Assert . IsTrue ( meetingEventsPostCount == meetingEventsPreCount + 1 ) ;
144151 Assert . IsTrue ( allCopilotEventsPostCount == allCopilotEventsPreCount + 4 ) ; // 4 new events - 1 meeting, 1 file, 1 chat, 1 outlook
145152 }
146153 }
147154
155+ /// <summary>
156+ /// When there's no permissions to read files/meetings, we should still save the chat at least
157+ /// </summary>
158+ [ TestMethod ]
159+ public async Task CopilotEventManagerWithNoPermissionsSaveTest ( )
160+ {
161+
162+ using ( var db = new AnalyticsEntitiesContext ( ) )
163+ {
164+ db . CopilotEventMetadataFiles . RemoveRange ( db . CopilotEventMetadataFiles ) ;
165+ db . CopilotEventMetadataMeetings . RemoveRange ( db . CopilotEventMetadataMeetings ) ;
166+ await db . SaveChangesAsync ( ) ;
167+
168+ // Counts before
169+ var fileEventsPreCount = await db . CopilotEventMetadataFiles . CountAsync ( ) ;
170+ var meetingEventsPreCount = await db . CopilotEventMetadataMeetings . CountAsync ( ) ;
171+ var allCopilotEventsPreCount = await db . CopilotChats . CountAsync ( ) ;
172+
173+ await ExecuteCopilotEventManagerSaveFlow ( new ReturnNullFilesAndMeetingsAdaptor ( ) , db ) ;
174+
175+ // Counts after
176+ var fileEventsPostCount = await db . CopilotEventMetadataFiles . CountAsync ( ) ;
177+ var meetingEventsPostCount = await db . CopilotEventMetadataMeetings . CountAsync ( ) ;
178+ var allCopilotEventsPostCount = await db . CopilotChats . CountAsync ( ) ;
179+
180+ // Assertions
181+ Assert . IsTrue ( fileEventsPostCount == fileEventsPreCount ) ; // No file data so no new file event
182+ Assert . IsTrue ( meetingEventsPostCount == meetingEventsPreCount ) ; // No meeting data so no new meeting event
183+ Assert . IsTrue ( allCopilotEventsPostCount == allCopilotEventsPreCount + 4 ) ; // 4 new events - 1 meeting, 1 file, 1 chat, 1 outlook
184+ }
185+ }
186+
148187 /// <summary>
149188 /// Tests we can load metadata from Graph
150189 /// </summary>
0 commit comments