Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/dotnet-core.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ jobs:
run: dotnet restore
- name: Build
run: dotnet build --configuration Release --no-restore
- name: Test
run: dotnet test --configuration Release --no-build --verbosity normal
- name: Publish
run: dotnet publish --configuration Release --no-build --output ./.github/out
- name: Delete extra files
Expand Down
105 changes: 105 additions & 0 deletions aliyun-ddns.Tests/InstanceCreatorTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Xunit;
using aliyun_ddns.Common;
using aliyun_ddns.IPGetter;
using aliyun_ddns.IPGetter.IPv4Getter;
using aliyun_ddns.IPGetter.IPv6Getter;

namespace aliyun_ddns.Tests
{
public class InstanceCreatorTests
{
[Fact]
public void Create_WithIIPv4Getter_ShouldReturnInstances()
{
// Act
var instances = InstanceCreator.Create<IIPv4Getter>();

// Assert
Assert.NotNull(instances);
Assert.NotEmpty(instances);
}

[Fact]
public void Create_WithIIPv6Getter_ShouldReturnInstances()
{
// Act
var instances = InstanceCreator.Create<IIPv6Getter>();

// Assert
Assert.NotNull(instances);
Assert.NotEmpty(instances);
}

[Fact]
public void Create_WithIIPGetter_ShouldReturnInstances()
{
// Act
var instances = InstanceCreator.Create<IIPGetter>();

// Assert
Assert.NotNull(instances);
Assert.NotEmpty(instances);
}

[Fact]
public void Create_ShouldNotReturnAbstractClasses()
{
// Act
var instances = InstanceCreator.Create<IIPGetter>();

// Assert
Assert.All(instances, instance =>
Assert.False(instance.GetType().IsAbstract));
}

[Fact]
public void Create_ShouldNotReturnInterfaces()
{
// Act
var instances = InstanceCreator.Create<IIPGetter>();

// Assert
Assert.All(instances, instance =>
Assert.False(instance.GetType().IsInterface));
}

[Fact]
public void Create_WithFilter_ShouldFilterInstances()
{
// Act - filter to only include types whose name contains "Local"
var allInstances = InstanceCreator.Create<IIPGetter>();
var filteredInstances = InstanceCreator.Create<IIPGetter>(t => t.Name.Contains("Local"));

// Assert
Assert.True(filteredInstances.Count() <= allInstances.Count());
Assert.All(filteredInstances, instance =>
Assert.Contains("Local", instance.GetType().Name));
}

[Fact]
public void Create_WithNullFilter_ShouldReturnAllInstances()
{
// Act
var instances1 = InstanceCreator.Create<IIPGetter>(null);
var instances2 = InstanceCreator.Create<IIPGetter>();

// Assert
Assert.Equal(instances1.Count(), instances2.Count());
}
}

// Test interface for testing
public interface ITestInterface
{
string GetValue();
}

// Test implementation for testing
public class TestImplementation : ITestInterface
{
public string GetValue() => "test";
}
Comment on lines +93 to +104

Copilot AI Dec 25, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test interface ITestInterface and test implementation TestImplementation are defined but never used in any tests. These appear to be remnants from development and should be removed to keep the test file clean and maintainable.

Suggested change
// Test interface for testing
public interface ITestInterface
{
string GetValue();
}
// Test implementation for testing
public class TestImplementation : ITestInterface
{
public string GetValue() => "test";
}

Copilot uses AI. Check for mistakes.
}
90 changes: 90 additions & 0 deletions aliyun-ddns.Tests/LogTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
using System;
using System.IO;
using Xunit;
using aliyun_ddns.Common;
using aliyun_ddns;

namespace aliyun_ddns.Tests
{
public class LogTests
{
[Fact]
public void Print_ShouldOutputToConsole()
{
// Arrange
var originalOut = Console.Out;
using var stringWriter = new StringWriter();
Console.SetOut(stringWriter);

// Act
Log.Print("Test message");

// Assert
var output = stringWriter.ToString();
Assert.Contains("Test message", output);
Assert.Contains("[", output); // Should contain timestamp brackets
Assert.Contains("]", output);

// Cleanup
Console.SetOut(originalOut);
}

[Fact]
public void Print_ShouldIncludeTimestamp()
{
// Arrange
var originalOut = Console.Out;
using var stringWriter = new StringWriter();
Console.SetOut(stringWriter);

// Act
Log.Print("Message with timestamp");

// Assert
var output = stringWriter.ToString();
// Output format should be: [MM/DD/YYYY HH:MM:SS]Message or [YYYY-MM-DD HH:MM:SS]Message
Assert.Matches(@"\[\d{1,4}", output);

// Cleanup
Console.SetOut(originalOut);
}

[Fact]
public void Print_WithEmptyString_ShouldStillIncludeTimestamp()
{
// Arrange
var originalOut = Console.Out;
using var stringWriter = new StringWriter();
Console.SetOut(stringWriter);

// Act
Log.Print("");

// Assert
var output = stringWriter.ToString();
Assert.Contains("[", output);
Assert.Contains("]", output);

// Cleanup
Console.SetOut(originalOut);
}

[Fact]
public void Print_WithNullString_ShouldNotThrowException()
{
// Arrange
var originalOut = Console.Out;
using var stringWriter = new StringWriter();
Console.SetOut(stringWriter);

// Act & Assert
var exception = Record.Exception(() => Log.Print(null));

// Cleanup
Console.SetOut(originalOut);

// The exception might be thrown by string concatenation, which is acceptable
// We're just ensuring the method can be called
Comment on lines +80 to +87

Copilot AI Dec 25, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assignment to exception is useless, since its value is never read.

Suggested change
// Act & Assert
var exception = Record.Exception(() => Log.Print(null));
// Cleanup
Console.SetOut(originalOut);
// The exception might be thrown by string concatenation, which is acceptable
// We're just ensuring the method can be called
// Act
var exception = Record.Exception(() => Log.Print(null));
// Assert
Assert.Null(exception);
// Cleanup
Console.SetOut(originalOut);

Copilot uses AI. Check for mistakes.
}
}
}
89 changes: 89 additions & 0 deletions aliyun-ddns.Tests/OptionsTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
using System;
using Xunit;
using aliyun_ddns;

namespace aliyun_ddns.Tests
{
public class OptionsTests
{
[Fact]
public void Options_DefaultValues_ShouldBeSet()
{
// Arrange & Act
var options = new Options();

// Assert
Assert.Equal("access key id", options.Akid);
Assert.Equal("access key secret", options.Aksct);
Assert.Equal("my.domain.com", options.Domain);
Assert.Null(options.RootDomain);
Assert.Equal(300, options.Redo);
Assert.Equal(600, options.TTL);
Assert.Equal(8.0, options.Timezone);
Assert.Equal("A,AAAA", options.Type);
Assert.False(options.CNIPv4);
Assert.Null(options.WebHook);
Assert.False(options.CheckLocalNetworkAdaptor);
Assert.Null(options.IPv4Networks);
Assert.Null(options.IPv6Networks);
}

[Fact]
public void Options_PropertySetters_ShouldWork()
{
// Arrange
var options = new Options();

// Act
options.Akid = "test-key-id";
options.Aksct = "test-key-secret";
options.Domain = "test.example.com";
options.RootDomain = "example.com";
options.Redo = 600;
options.TTL = 300;
options.Timezone = 5.5;
options.Type = "A";
options.CNIPv4 = true;
options.WebHook = "https://example.com/webhook";
options.CheckLocalNetworkAdaptor = true;
options.IPv4Networks = "192.168.1.0/24";
options.IPv6Networks = "240e::/16";

// Assert
Assert.Equal("test-key-id", options.Akid);
Assert.Equal("test-key-secret", options.Aksct);
Assert.Equal("test.example.com", options.Domain);
Assert.Equal("example.com", options.RootDomain);
Assert.Equal(600, options.Redo);
Assert.Equal(300, options.TTL);
Assert.Equal(5.5, options.Timezone);
Assert.Equal("A", options.Type);
Assert.True(options.CNIPv4);
Assert.Equal("https://example.com/webhook", options.WebHook);
Assert.True(options.CheckLocalNetworkAdaptor);
Assert.Equal("192.168.1.0/24", options.IPv4Networks);
Assert.Equal("240e::/16", options.IPv6Networks);
}

[Fact]
public void Options_Instance_ShouldNotBeNull()
{
// Act
var instance = Options.Instance;

// Assert
Assert.NotNull(instance);
}

[Fact]
public void Options_Instance_ShouldBeSingleton()
{
// Act
var instance1 = Options.Instance;
var instance2 = Options.Instance;

// Assert
Assert.Same(instance1, instance2);
}
}
}
Loading
Loading