- #578: Abgebrochene Laeufe (Schrittgrenze, Zeitueberschreitung, Abbruch, Ausnahme) tragen ihre bezahlten Token - vorher 0 in RunUsage, fuer das Budget unsichtbar. - #580: WebFetch prueft die aufgeloeste Zieladresse beim Verbinden; ein erlaubter Name, der auf 127.0.0.1 aufloest, wird gesperrt. - #575: Dateien entstehen unter Unix mit 0600 (AtomicFile, Protokolle, FileRW-Anhaengen, state.db), Protokollverzeichnisse mit 0700. - #576: SIGTERM/SIGINT beenden auch mit Fenster geordnet. - #593: OpenRouter-Statusdienst wird gestartet. - #589: Auftragsnachricht nennt aktuelle Zeit, Zeitzone und Termin. - #581: Unvollstaendiges Argument-JSON erreicht das Werkzeug nicht mehr; das Modell bekommt eine verstaendliche Rueckmeldung statt Parsertext. - #591: Weboberflaeche liest die Felder, die API und Ereignisstrom liefern (Freigaben, Protokoll, Taskboard mit Termin, Werkzeug-Einstellungen, Tokennutzung); Ereignisstrom in camelCase. - #592: Chat zeigt Nachrichten nicht mehr doppelt. - #536 (teilweise): A2A-Antworten tragen Absender und Empfaenger, Parser wirft nicht mehr; Datums- und Volltextfilter in Agenten-Chats. - deploy/publish.py: Packager-Pfad net10.0, Schalter --agent-file. - Version 0.1.4. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
75 lines
2.4 KiB
C#
75 lines
2.4 KiB
C#
using ClawdDotNet.Core.Storage;
|
|
using ClawdDotNet.Core.Tests.Infrastructure;
|
|
using Shouldly;
|
|
|
|
namespace ClawdDotNet.Core.Tests.Storage;
|
|
|
|
/// <summary>
|
|
/// #575: Auf der Test-VM (umask 0002) ging InstanceSettings.json beim ersten Speichern von
|
|
/// 0600 auf 0664, Protokolle entstanden mit 0664. Unter Windows nicht aussagekraeftig.
|
|
/// </summary>
|
|
public sealed class OwnerOnlyFileTests : IDisposable
|
|
{
|
|
private readonly string _dir = Path.Combine(Path.GetTempPath(), "clawd-owner-" + Guid.NewGuid().ToString("N")[..8]);
|
|
|
|
public OwnerOnlyFileTests() => Directory.CreateDirectory(_dir);
|
|
|
|
public void Dispose()
|
|
{
|
|
try { Directory.Delete(_dir, recursive: true); } catch { /* Aufraeumen ist best effort */ }
|
|
}
|
|
|
|
private const UnixFileMode NurBesitzer = UnixFileMode.UserRead | UnixFileMode.UserWrite;
|
|
|
|
[LinuxFact]
|
|
[System.Runtime.Versioning.SupportedOSPlatform("linux")]
|
|
public void AtomicFile_ersetzt_eine_offene_Datei_durch_eine_nur_fuer_den_Besitzer()
|
|
{
|
|
var pfad = Path.Combine(_dir, "InstanceSettings.json");
|
|
File.WriteAllText(pfad, "{}");
|
|
File.SetUnixFileMode(pfad, NurBesitzer | UnixFileMode.GroupRead | UnixFileMode.OtherRead);
|
|
|
|
AtomicFile.WriteAllText(pfad, "{\"a\":1}");
|
|
|
|
File.GetUnixFileMode(pfad).ShouldBe(NurBesitzer);
|
|
}
|
|
|
|
[LinuxFact]
|
|
[System.Runtime.Versioning.SupportedOSPlatform("linux")]
|
|
public void AtomicFile_legt_neue_Dateien_nur_fuer_den_Besitzer_an()
|
|
{
|
|
var pfad = Path.Combine(_dir, "neu.json");
|
|
|
|
AtomicFile.WriteAllText(pfad, "{}");
|
|
|
|
File.GetUnixFileMode(pfad).ShouldBe(NurBesitzer);
|
|
}
|
|
|
|
[LinuxFact]
|
|
[System.Runtime.Versioning.SupportedOSPlatform("linux")]
|
|
public void OpenAppend_legt_neue_Dateien_nur_fuer_den_Besitzer_an()
|
|
{
|
|
var pfad = Path.Combine(_dir, "protokoll.log");
|
|
|
|
using (var stream = OwnerOnlyFile.OpenAppend(pfad))
|
|
stream.WriteByte((byte)'x');
|
|
|
|
File.GetUnixFileMode(pfad).ShouldBe(NurBesitzer);
|
|
}
|
|
|
|
[Fact]
|
|
public void OpenAppend_haengt_an_statt_zu_ueberschreiben()
|
|
{
|
|
var pfad = Path.Combine(_dir, "anhang.txt");
|
|
|
|
foreach (var teil in new[] { "eins", "zwei" })
|
|
{
|
|
using var stream = OwnerOnlyFile.OpenAppend(pfad);
|
|
using var writer = new StreamWriter(stream);
|
|
writer.Write(teil);
|
|
}
|
|
|
|
File.ReadAllText(pfad).ShouldBe("einszwei");
|
|
}
|
|
}
|