Skip to content

Commit 3109b2c

Browse files
committed
fix(import): reject empty Gemini Takeout prompt blocks
1 parent cbf59b8 commit 3109b2c

3 files changed

Lines changed: 26 additions & 1 deletion

File tree

internal/parser/gemini_apps_takeout.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -526,8 +526,11 @@ func splitGeminiAppsBlocks(tokens []html.Token) []string {
526526
var block []html.Token
527527
blockDepth := 0
528528
flush := func() {
529+
if block == nil {
530+
return
531+
}
529532
value := strings.TrimSpace(renderGeminiAppsTokens(block))
530-
if value != "" && !geminiAppsTimestampRE.MatchString(value) {
533+
if !geminiAppsTimestampRE.MatchString(value) {
531534
blocks = append(blocks, value)
532535
}
533536
block = nil

internal/parser/gemini_apps_takeout_test.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,26 @@ func TestParseGeminiAppsBrStaysInsidePromptBlock(t *testing.T) {
143143
assert.Equal(t, "answer", results[0].Messages[1].Content)
144144
}
145145

146+
func TestParseGeminiAppsEmptyFirstContentBlockIsError(t *testing.T) {
147+
fixture := `<!doctype html><html><head><title>My Activity History</title></head><body>
148+
<div class="outer-cell"><div class="header-cell"><h3>Gemini Apps</h3><p>Prompted</p><p>Jan 2, 2025, 3:04:05 PM EDT</p></div><div class="content-cell"><p></p><p>answer</p></div></div>
149+
</body></html>`
150+
path := filepath.Join(t.TempDir(), "empty-first-block.html")
151+
require.NoError(t, os.WriteFile(path, []byte(fixture), 0o644))
152+
153+
provider, ok := NewProvider(AgentGeminiApps, ProviderConfig{})
154+
require.True(t, ok)
155+
exporter := provider.(GeminiAppsExportParser)
156+
var results []ParseResult
157+
summary, err := exporter.ParseGeminiAppsExport(path, func(result ParseResult) error {
158+
results = append(results, result)
159+
return nil
160+
})
161+
assert.ErrorContains(t, err, "no admissible Prompted records")
162+
assert.Empty(t, results)
163+
assert.Equal(t, 1, summary.Errors)
164+
}
165+
146166
func TestParseGeminiAppsTimestampMustBeInHeader(t *testing.T) {
147167
fixture := strings.Replace(
148168
sanitizedGeminiAppsHTML,

internal/parser/import_only_provider_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,13 @@ func TestImportOnlyProviderExportCapabilitiesAreAgentSpecific(t *testing.T) {
1212
require.True(t, ok)
1313
assert.Implements(t, (*ChatGPTExportParser)(nil), chatGPTProvider)
1414
assert.NotImplements(t, (*ClaudeAIExportParser)(nil), chatGPTProvider)
15+
assert.NotImplements(t, (*GeminiAppsExportParser)(nil), chatGPTProvider)
1516

1617
claudeAIProvider, ok := NewProvider(AgentClaudeAI, ProviderConfig{})
1718
require.True(t, ok)
1819
assert.Implements(t, (*ClaudeAIExportParser)(nil), claudeAIProvider)
1920
assert.NotImplements(t, (*ChatGPTExportParser)(nil), claudeAIProvider)
21+
assert.NotImplements(t, (*GeminiAppsExportParser)(nil), claudeAIProvider)
2022

2123
geminiAppsProvider, ok := NewProvider(AgentGeminiApps, ProviderConfig{})
2224
require.True(t, ok)

0 commit comments

Comments
 (0)