using System.Net;
using Sunfish.Config;
namespace Sunfish.Tests;
public class ConfigTests
{
private const string Sample = """
# Comentario de cabecera
[Server]
Name = Pruebas
HttpPort = 9090
; otro comentario
Bind = 127.0.0.1
[Share web]
Location = /
Path = C:\www
Listing = false
[Share fotos]
Location = fotos/
Path = D:\Fotos
Upload = yes
Users = ana, pepe
""";
[Fact]
public void ParsesValuesAndDefaults()
{
var config = SunfishConfig.Parse(Sample);
Assert.Empty(config.Errors);
Assert.Equal("Pruebas", config.Server.Name);
Assert.Equal(9090, config.Server.HttpPort);
Assert.Equal([IPAddress.Loopback], config.Server.Bind);
Assert.Equal(2121, config.Server.FtpPort);
var web = config.Shares[0];
Assert.Equal("web", web.Name);
Assert.False(web.Listing);
Assert.Equal("index.html", web.Index);
Assert.True(web.Mac);
Assert.True(web.WebDav);
var fotos = config.Shares[1];
Assert.Equal("/fotos", fotos.Location);
Assert.True(fotos.Upload);
Assert.False(fotos.Delete);
Assert.Equal(["ana", "pepe"], fotos.Users);
}
[Fact]
public void RewritingKeepsCommentsAndOrder()
{
var config = SunfishConfig.Parse(Sample);
config.Server.HttpPort = 8181;
config.Shares[1].Delete = true;
string text = config.ToText().Replace("\r\n", "\n");
Assert.Contains("# Comentario de cabecera", text);
Assert.Contains("; otro comentario", text);
Assert.Contains("HttpPort = 8181", text);
Assert.Contains("Delete = true", text);
Assert.True(text.IndexOf("[Share web]", StringComparison.Ordinal) < text.IndexOf("[Share fotos]", StringComparison.Ordinal));
// Los valores por defecto que no estaban no se anaden.
Assert.DoesNotContain("Mac =", text);
}
[Fact]
public void RoundTripWithoutChangesIsIdentical()
{
var config = SunfishConfig.Parse(Sample);
string normalizedSample = Sample.Replace("\r\n", "\n") + "\n";
// Location se normaliza al leer ("fotos/" -> "/fotos"): es el unico cambio esperado.
string expected = normalizedSample.Replace("Location = fotos/", "Location = /fotos");
Assert.Equal(expected, config.ToText().Replace("\r\n", "\n"));
}
[Fact]
public void AddingAndRemovingShares()
{
var config = SunfishConfig.Parse(Sample);
config.Shares.RemoveAt(0);
config.Shares.Add(new ShareConfig { Name = "musica", Location = "/musica", Path = "E:\\Musica", Zip = true });
string text = config.ToText().Replace("\r\n", "\n");
Assert.DoesNotContain("[Share web]", text);
Assert.Contains("[Share musica]\nLocation = /musica\nPath = E:\\Musica\nZip = true\n", text);
var reread = SunfishConfig.Parse(text);
Assert.Equal(["fotos", "musica"], reread.Shares.Select(s => s.Name));
}
[Fact]
public void ReportsErrorsWithLineNumbers()
{
var config = SunfishConfig.Parse("""
[Server]
HttpPort = muchos
esto no es nada
[Share a]
Location = /x
[Share b]
Location = /x
Path = C:\b
[Raro]
""");
Assert.Contains(config.Errors, e => e.Contains("2") && e.Contains("muchos"));
Assert.Contains(config.Errors, e => e.Contains("3"));
Assert.Contains(config.Errors, e => e.Contains("Path"));
Assert.Contains(config.Errors, e => e.Contains("Raro"));
Assert.Equal(8080, config.Server.HttpPort);
Assert.False(config.Shares[0].Enabled); // sin Path
Assert.True(config.Shares[1].Enabled); // la location ya no esta ocupada por un share activo
}
[Fact]
public void RejectsReservedAndInvalidLocations()
{
var config = SunfishConfig.Parse("""
[Share a]
Location = /$sunfish/x
Path = C:\a
[Share b]
Location = /a/../b
Path = C:\b
""");
Assert.All(config.Shares, s => Assert.False(s.Enabled));
Assert.Equal(2, config.Errors.Count);
}
[Fact]
public void SaveIsAtomicAndReadable()
{
using var dir = new TempDir();
string path = Path.Combine(dir.Path, "sub", "sunfish.conf");
var config = new SunfishConfig();
config.Shares.Add(new ShareConfig { Name = "x", Location = "/x", Path = dir.Path });
config.Save(path);
Assert.True(File.Exists(path));
Assert.False(File.Exists(path + ".tmp"));
var loaded = SunfishConfig.Load(path);
Assert.Equal("/x", loaded.Shares.Single().Location);
}
}