114 lines
4.1 KiB
C#
114 lines
4.1 KiB
C#
using System;
|
|
using System.IO;
|
|
using Paca.Browser;
|
|
using Xunit;
|
|
|
|
namespace Paca.Tests;
|
|
|
|
public sealed class BrowserStorageAndSessionHardeningPhase7Tests : IDisposable
|
|
{
|
|
private readonly string _tempDir;
|
|
|
|
public BrowserStorageAndSessionHardeningPhase7Tests()
|
|
{
|
|
_tempDir = Path.Combine(Path.GetTempPath(), "paca_phase7_test_" + Guid.NewGuid().ToString("N"));
|
|
Directory.CreateDirectory(_tempDir);
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
try
|
|
{
|
|
if (Directory.Exists(_tempDir))
|
|
{
|
|
Directory.Delete(_tempDir, true);
|
|
}
|
|
}
|
|
catch { }
|
|
}
|
|
|
|
[Fact]
|
|
public void Gate701_WorkspaceRoutingRules_Preserves_W_Prefixed_Domains_And_Handles_Null()
|
|
{
|
|
// Arrange
|
|
var rulesFile = Path.Combine(_tempDir, "routing_rules.json");
|
|
var rules = new WorkspaceRoutingRules(rulesFile);
|
|
|
|
// Act - domains starting with 'w'
|
|
rules.Add("wired.com", "TechNews");
|
|
rules.Add("whatsapp.com", "Messaging");
|
|
rules.Add("www.wsj.com", "Finance");
|
|
rules.Add("weather.com", "Utility");
|
|
|
|
// Assert - must NOT corrupt domains like 'wired.com' -> 'ired.com'
|
|
Assert.Equal("TechNews", rules.Match("https://wired.com/story/ai"));
|
|
Assert.Equal("TechNews", rules.Match("https://www.wired.com/story/ai"));
|
|
Assert.Equal("Messaging", rules.Match("https://web.whatsapp.com"));
|
|
Assert.Equal("Messaging", rules.Match("https://whatsapp.com"));
|
|
Assert.Equal("Finance", rules.Match("https://wsj.com"));
|
|
Assert.Equal("Utility", rules.Match("https://weather.com/today"));
|
|
|
|
// Null & Malformed safety
|
|
Assert.Null(rules.Match(null));
|
|
Assert.Null(rules.Match(""));
|
|
Assert.Null(rules.Match("javascript:alert(1)"));
|
|
Assert.Null(rules.Match("not-a-url"));
|
|
}
|
|
|
|
[Fact]
|
|
public void Gate702_NotesStore_Ensures_Directory_On_Delete_And_Explicit_Save()
|
|
{
|
|
// Arrange: deep nested path whose parent directory doesn't exist yet
|
|
var deepNotesPath = Path.Combine(_tempDir, "nested", "sub", "notes.json");
|
|
var store = new NotesStore(deepNotesPath);
|
|
|
|
// Act & Assert 1: Add creates file & directory
|
|
var id1 = store.Add("First Note", "Content 1");
|
|
Assert.True(File.Exists(deepNotesPath));
|
|
Assert.Single(store.List());
|
|
|
|
// Now simulate directory deletion or separate store instance targeting a new path
|
|
var deepNotesPath2 = Path.Combine(_tempDir, "another", "sub", "notes2.json");
|
|
var store2 = new NotesStore(deepNotesPath2);
|
|
|
|
// Act 2: Explicit Save without prior Add should ensure directory and not throw
|
|
store2.Save();
|
|
Assert.True(File.Exists(deepNotesPath2));
|
|
|
|
// Act 3: Delete should ensure directory and persist
|
|
store.Delete(id1);
|
|
Assert.Empty(store.List());
|
|
var reloaded = new NotesStore(deepNotesPath);
|
|
Assert.Empty(reloaded.List());
|
|
}
|
|
|
|
[Fact]
|
|
public void Gate703_TrackerCounter_And_PermissionGate_Handle_Malformed_Urls_And_Null_Safely()
|
|
{
|
|
// Arrange
|
|
var counter = new TrackerCounter();
|
|
var gate = new PermissionGate();
|
|
|
|
// Act & Assert 1: TrackerCounter should not throw UriFormatException on malformed or relative URLs
|
|
var initialTotal = counter.Total;
|
|
counter.Record("https://google-analytics.com/collect");
|
|
Assert.Equal(initialTotal + 1, counter.Total);
|
|
Assert.True(counter.ByDomain.ContainsKey("google-analytics.com"));
|
|
|
|
// Malformed URLs must be safely handled without throwing uncaught exceptions
|
|
counter.Record("malformed-not-an-url");
|
|
counter.Record("");
|
|
counter.Record("javascript:void(0)");
|
|
|
|
// Act & Assert 2: PermissionGate handles null or whitespace gracefully
|
|
var verdict1 = gate.Decide("Camera", "");
|
|
Assert.Equal(PermissionVerdict.Ask, verdict1);
|
|
|
|
var verdict2 = gate.Decide("", "https://example.com");
|
|
Assert.Equal(PermissionVerdict.Ask, verdict2);
|
|
|
|
gate.ResetOrigin(null!);
|
|
var allOrigins = gate.GetAllOrigins();
|
|
Assert.NotNull(allOrigins);
|
|
}
|
|
}
|