Skip to content

Commit 2cbe87f

Browse files
feat(audience): add ConsentStore for atomic consent persistence
Introduces Core/ConsentStore, a tiny static wrapper that reads and writes the user's ConsentLevel to disk under {persistentDataPath}/imtbl_audience/consent. Save uses the same write-temp-then-move pattern as DiskStore and Identity so a crash mid-write cannot leave a half-written consent level on disk; Load returns null on missing, malformed, or unreadable files so callers can fall back to the configured default. No production callers yet. ImmutableAudience will consume this in the next commit to persist SetConsent decisions across launches. Tests/Runtime/Core/ConsentStoreTests covers the round-trip, the null return for missing/corrupt/out-of-range files, and the behaviour when the backing directory does not yet exist.
1 parent be1f22c commit 2cbe87f

2 files changed

Lines changed: 120 additions & 0 deletions

File tree

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
using System;
2+
using System.IO;
3+
4+
namespace Immutable.Audience
5+
{
6+
internal static class ConsentStore
7+
{
8+
internal static void Save(string persistentDataPath, ConsentLevel level)
9+
{
10+
Directory.CreateDirectory(AudiencePaths.AudienceDir(persistentDataPath));
11+
12+
var filePath = AudiencePaths.ConsentFile(persistentDataPath);
13+
var tmpPath = filePath + ".tmp";
14+
15+
File.WriteAllText(tmpPath, ((int)level).ToString());
16+
17+
try
18+
{
19+
File.Move(tmpPath, filePath);
20+
}
21+
catch (IOException)
22+
{
23+
File.Delete(filePath);
24+
File.Move(tmpPath, filePath);
25+
}
26+
}
27+
28+
// Returns null on missing/malformed/unreadable file; caller falls back to config default.
29+
internal static ConsentLevel? Load(string persistentDataPath)
30+
{
31+
try
32+
{
33+
var filePath = AudiencePaths.ConsentFile(persistentDataPath);
34+
if (!File.Exists(filePath)) return null;
35+
36+
var text = File.ReadAllText(filePath).Trim();
37+
if (int.TryParse(text, out var raw) && Enum.IsDefined(typeof(ConsentLevel), raw))
38+
return (ConsentLevel)raw;
39+
}
40+
catch (IOException)
41+
{
42+
}
43+
catch (UnauthorizedAccessException)
44+
{
45+
}
46+
47+
return null;
48+
}
49+
}
50+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
using System.IO;
2+
using NUnit.Framework;
3+
4+
namespace Immutable.Audience.Tests
5+
{
6+
[TestFixture]
7+
internal class ConsentStoreTests
8+
{
9+
private string _testDir;
10+
11+
[SetUp]
12+
public void SetUp()
13+
{
14+
_testDir = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
15+
Directory.CreateDirectory(_testDir);
16+
}
17+
18+
[TearDown]
19+
public void TearDown()
20+
{
21+
if (Directory.Exists(_testDir))
22+
Directory.Delete(_testDir, recursive: true);
23+
}
24+
25+
[Test]
26+
public void Load_NoFile_ReturnsNull()
27+
{
28+
Assert.IsNull(ConsentStore.Load(_testDir));
29+
}
30+
31+
[Test]
32+
public void SaveThenLoad_RoundtripsValue([Values] ConsentLevel level)
33+
{
34+
ConsentStore.Save(_testDir, level);
35+
Assert.AreEqual(level, ConsentStore.Load(_testDir));
36+
}
37+
38+
[Test]
39+
public void Save_OverwritesPreviousValue()
40+
{
41+
ConsentStore.Save(_testDir, ConsentLevel.Anonymous);
42+
ConsentStore.Save(_testDir, ConsentLevel.Full);
43+
44+
Assert.AreEqual(ConsentLevel.Full, ConsentStore.Load(_testDir));
45+
}
46+
47+
[Test]
48+
public void Load_MalformedFile_ReturnsNull()
49+
{
50+
// A garbage value that isn't a valid enum int.
51+
var dir = AudiencePaths.AudienceDir(_testDir);
52+
Directory.CreateDirectory(dir);
53+
File.WriteAllText(AudiencePaths.ConsentFile(_testDir), "not-an-int");
54+
55+
Assert.IsNull(ConsentStore.Load(_testDir));
56+
}
57+
58+
[Test]
59+
public void Load_OutOfRangeIntInFile_ReturnsNull()
60+
{
61+
// 999 is parseable as int but not a defined ConsentLevel.
62+
var dir = AudiencePaths.AudienceDir(_testDir);
63+
Directory.CreateDirectory(dir);
64+
File.WriteAllText(AudiencePaths.ConsentFile(_testDir), "999");
65+
66+
Assert.IsNull(ConsentStore.Load(_testDir));
67+
}
68+
69+
}
70+
}

0 commit comments

Comments
 (0)