@@ -11,11 +11,12 @@ import (
1111)
1212
1313type fakeHydrationSource struct {
14- blocks map [string ]* Block
15- children map [string ]map [string ]* BlockPage
16- markdown * MarkdownPage
17- users map [string ]* UserPage
18- usersErr error
14+ blocks map [string ]* Block
15+ children map [string ]map [string ]* BlockPage
16+ markdown * MarkdownPage
17+ users map [string ]* UserPage
18+ usersErr error
19+ usersCalls int
1920}
2021
2122func (f * fakeHydrationSource ) RetrieveBlock (_ context.Context , id string ) (* Block , error ) {
@@ -39,6 +40,7 @@ func (f *fakeHydrationSource) RetrievePageMarkdown(_ context.Context, _ string,
3940}
4041
4142func (f * fakeHydrationSource ) ListUsers (_ context.Context , cursor string ) (* UserPage , error ) {
43+ f .usersCalls ++
4244 if f .usersErr != nil {
4345 return nil , f .usersErr
4446 }
@@ -111,7 +113,7 @@ func completeHydrationSource() *fakeHydrationSource {
111113 "" : {
112114 Results : []User {
113115 {ID : "user-1" , Name : "Test Attendee" , Type : "person" , Person : UserPerson {Email : "attendee@example.com" , EmailVerified : true }},
114- {ID : "user-2" , Name : "Unresolved Attendee" , Type : "person" },
116+ {ID : "user-2" , Name : "Unresolved Attendee" , Type : "person" , Person : UserPerson { Email : "unverified@example.com" } },
115117 },
116118 },
117119 },
@@ -152,6 +154,8 @@ func TestHydratorBuildsCanonicalSnapshotWithoutInventingOrganizer(t *testing.T)
152154 assert .Equal (RawFormat , snapshot .RawFormat )
153155 assert .Contains (string (snapshot .Raw ), `"discovery"` )
154156 assert .Contains (string (snapshot .Raw ), `"page_markdown"` )
157+ assert .Contains (string (snapshot .Raw ), `"attendee_labels":["Test Attendee","Unresolved Attendee"]` )
158+ assert .NotContains (string (snapshot .Raw ), "unverified@example.com" )
155159 assert .NotContains (string (snapshot .Raw ), "user@example.com" , "configured identity must not enter provider evidence" )
156160 assert .Contains (string (snapshot .Metadata ), `"creator_user_id":"creator-1"` )
157161 assert .Contains (string (snapshot .Metadata ), `"unresolved_attendee_ids":["user-2"]` )
@@ -171,6 +175,66 @@ func TestHydratorUsesMarkdownTranscriptFallback(t *testing.T) {
171175 assert .Contains (hydrated .Warnings , "structured transcript was empty; used page Markdown transcript" )
172176}
173177
178+ func TestHydratorLeavesIncompleteMarkdownTranscriptPending (t * testing.T ) {
179+ tests := []struct {
180+ name string
181+ mutate func (* MarkdownPage )
182+ }{
183+ {
184+ name : "truncated" ,
185+ mutate : func (page * MarkdownPage ) {
186+ page .Truncated = true
187+ },
188+ },
189+ {
190+ name : "unknown blocks" ,
191+ mutate : func (page * MarkdownPage ) {
192+ page .UnknownBlockIDs = []string {"transcript-child-1" }
193+ },
194+ },
195+ }
196+
197+ for _ , tt := range tests {
198+ t .Run (tt .name , func (t * testing.T ) {
199+ assert := assert .New (t )
200+ source := completeHydrationSource ()
201+ empty := paragraph ("transcript-1" , "" , false )
202+ source .blocks ["transcript-1" ] = & empty
203+ source .markdown .Markdown = "# Weekly planning\n \n ## Transcript\n Test Speaker: Partial transcript."
204+ tt .mutate (source .markdown )
205+
206+ hydrated , err := NewHydrator (source ).Hydrate (context .Background (), hydrationMeeting ())
207+ require .NoError (t , err )
208+ assert .Empty (hydrated .Transcript )
209+ assert .False (hydrated .MarkdownTranscriptFallback )
210+ assert .Contains (hydrated .Warnings , "page Markdown transcript was incomplete; transcript remains pending" )
211+ })
212+ }
213+ }
214+
215+ func TestHydratorDoesNotCompareIncompleteMarkdownTranscript (t * testing.T ) {
216+ source := completeHydrationSource ()
217+ source .markdown .Markdown = "# Weekly planning\n \n ## Transcript\n Test Speaker: Partial conflicting transcript."
218+ source .markdown .Truncated = true
219+
220+ hydrated , err := NewHydrator (source ).Hydrate (context .Background (), hydrationMeeting ())
221+ require .NoError (t , err )
222+ assert .NotContains (t , hydrated .Warnings , "structured transcript and page Markdown transcript differed" )
223+ }
224+
225+ func TestHydratorDoesNotWarnAboutIncompleteMarkdownWithoutTranscript (t * testing.T ) {
226+ source := completeHydrationSource ()
227+ empty := paragraph ("transcript-1" , "" , false )
228+ source .blocks ["transcript-1" ] = & empty
229+ source .markdown .Markdown = "# Weekly planning\n \n Meeting context only."
230+ source .markdown .UnknownBlockIDs = []string {"unrelated-block-1" }
231+
232+ hydrated , err := NewHydrator (source ).Hydrate (context .Background (), hydrationMeeting ())
233+ require .NoError (t , err )
234+ assert .Empty (t , hydrated .Transcript )
235+ assert .NotContains (t , hydrated .Warnings , "page Markdown transcript was incomplete; transcript remains pending" )
236+ }
237+
174238func TestHydratorRejectsRepeatedChildCursor (t * testing.T ) {
175239 source := completeHydrationSource ()
176240 source .children ["notes-1" ]["" ] = & BlockPage {HasMore : true , NextCursor : "same" }
@@ -197,3 +261,23 @@ func TestHydratorDegradesWhenUserInformationIsUnavailable(t *testing.T) {
197261 require .NoError (err )
198262 assert .Contains (snapshot .Body , "Attendees: user-1, user-2" )
199263}
264+
265+ func TestHydratorDegradesAndCachesTransientUserListingFailure (t * testing.T ) {
266+ assert := assert .New (t )
267+ require := require .New (t )
268+ source := completeHydrationSource ()
269+ source .usersErr = errors .New ("rate limit exceeded" )
270+ hydrator := NewHydrator (source )
271+
272+ first , err := hydrator .Hydrate (context .Background (), hydrationMeeting ())
273+ require .NoError (err )
274+ assert .Empty (first .Attendees )
275+ assert .Equal ([]string {"user-1" , "user-2" }, first .AttendeeLabels )
276+ assert .Contains (first .Warnings , "Notion User Information lookup failed: rate limit exceeded; attendee emails were not resolved" )
277+
278+ second , err := hydrator .Hydrate (context .Background (), hydrationMeeting ())
279+ require .NoError (err )
280+ assert .Empty (second .Attendees )
281+ assert .Contains (second .Warnings , "Notion User Information lookup failed: rate limit exceeded; attendee emails were not resolved" )
282+ assert .Equal (1 , source .usersCalls )
283+ }
0 commit comments