diff --git a/src/SentenceStudio.Shared/AssemblyInfo.cs b/src/SentenceStudio.Shared/AssemblyInfo.cs
new file mode 100644
index 00000000..49394e0f
--- /dev/null
+++ b/src/SentenceStudio.Shared/AssemblyInfo.cs
@@ -0,0 +1,3 @@
+using System.Runtime.CompilerServices;
+
+[assembly: InternalsVisibleTo("SentenceStudio.UnitTests")]
diff --git a/tests/SentenceStudio.UnitTests/Services/ClassifyContentAsyncTests.cs b/tests/SentenceStudio.UnitTests/Services/ClassifyContentAsyncTests.cs
new file mode 100644
index 00000000..bb88b92c
--- /dev/null
+++ b/tests/SentenceStudio.UnitTests/Services/ClassifyContentAsyncTests.cs
@@ -0,0 +1,173 @@
+using FluentAssertions;
+using Microsoft.Data.Sqlite;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using Moq;
+using SentenceStudio.Abstractions;
+using SentenceStudio.Data;
+using SentenceStudio.Shared.Models;
+using SentenceStudio.Services;
+using Xunit;
+
+namespace SentenceStudio.UnitTests.Services;
+
+///
+/// Tests for ContentImportService.ClassifyContentAsync — covering all 3 branches:
+/// null AI response, AI exception, and the 4-arm type-switch (vocabulary/phrases/sentences/transcript + unknown).
+///
+public class ClassifyContentAsyncTests : IDisposable
+{
+ private readonly SqliteConnection _connection;
+ private readonly ServiceProvider _serviceProvider;
+ private readonly Mock _mockAiService;
+
+ public ClassifyContentAsyncTests()
+ {
+ _connection = new SqliteConnection("DataSource=:memory:");
+ _connection.Open();
+
+ var mockPreferences = new Mock();
+ mockPreferences.Setup(p => p.Get(It.IsAny(), It.IsAny()))
+ .Returns((string key, string defaultValue) => defaultValue);
+
+ var mockFileSystem = new Mock();
+ _mockAiService = new Mock();
+
+ var services = new ServiceCollection();
+
+ services.AddDbContext(options =>
+ {
+ options.UseSqlite(_connection);
+ options.ConfigureWarnings(w =>
+ w.Ignore(Microsoft.EntityFrameworkCore.Diagnostics.RelationalEventId.PendingModelChangesWarning));
+ });
+
+ services.AddLogging(builder => builder.AddDebug());
+ services.AddSingleton(mockPreferences.Object);
+ services.AddSingleton(new NoOpSyncService());
+ services.AddSingleton(mockFileSystem.Object);
+ services.AddSingleton(_mockAiService.Object);
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+
+ _serviceProvider = services.BuildServiceProvider();
+
+ using var scope = _serviceProvider.CreateScope();
+ var dbContext = scope.ServiceProvider.GetRequiredService();
+ dbContext.Database.EnsureCreated();
+ }
+
+ public void Dispose()
+ {
+ _serviceProvider.Dispose();
+ _connection.Close();
+ _connection.Dispose();
+ }
+
+ // -----------------------------------------------------------------
+ // Branch 1: null AI response → Vocabulary / 0.5f
+ // -----------------------------------------------------------------
+
+ [Fact]
+ public async Task ClassifyContent_NullAiResponse_DefaultsToVocabulary()
+ {
+ // Arrange
+ _mockAiService
+ .Setup(s => s.SendPrompt(It.IsAny()))
+ .ReturnsAsync((ContentClassificationAiResponse?)null);
+
+ using var scope = _serviceProvider.CreateScope();
+ var service = scope.ServiceProvider.GetRequiredService();
+
+ // Act
+ var result = await service.ClassifyContentAsync("apple,사과\nbanana,바나나", null);
+
+ // Assert
+ result.ContentType.Should().Be(ContentType.Vocabulary);
+ result.Confidence.Should().BeApproximately(0.5f, 0.001f);
+ }
+
+ // -----------------------------------------------------------------
+ // Branch 2: AI throws → Vocabulary / 0.3f with error signals
+ // -----------------------------------------------------------------
+
+ [Fact]
+ public async Task ClassifyContent_AiException_DefaultsWithLowConfidence()
+ {
+ // Arrange
+ _mockAiService
+ .Setup(s => s.SendPrompt(It.IsAny()))
+ .ThrowsAsync(new InvalidOperationException("AI unavailable"));
+
+ using var scope = _serviceProvider.CreateScope();
+ var service = scope.ServiceProvider.GetRequiredService();
+
+ // Act
+ var result = await service.ClassifyContentAsync("apple,사과\nbanana,바나나", null);
+
+ // Assert
+ result.ContentType.Should().Be(ContentType.Vocabulary);
+ result.Confidence.Should().BeApproximately(0.3f, 0.001f);
+ result.Signals.Should().Contain("error");
+ result.Signals.Should().Contain(nameof(InvalidOperationException));
+ }
+
+ // -----------------------------------------------------------------
+ // Branch 3: type-switch arms
+ // -----------------------------------------------------------------
+
+ [Theory]
+ [InlineData("vocabulary", ContentType.Vocabulary)]
+ [InlineData("phrases", ContentType.Phrases)]
+ [InlineData("sentences", ContentType.Sentences)]
+ [InlineData("transcript", ContentType.Transcript)]
+ public async Task ClassifyContent_KnownType_MapsCorrectly(string aiType, ContentType expectedType)
+ {
+ // Arrange
+ _mockAiService
+ .Setup(s => s.SendPrompt(It.IsAny()))
+ .ReturnsAsync(new ContentClassificationAiResponse
+ {
+ Type = aiType,
+ Confidence = 0.9f,
+ Reasoning = $"Looks like {aiType}",
+ Signals = new List { aiType }
+ });
+
+ using var scope = _serviceProvider.CreateScope();
+ var service = scope.ServiceProvider.GetRequiredService();
+
+ // Act
+ var result = await service.ClassifyContentAsync("some content sample", null);
+
+ // Assert
+ result.ContentType.Should().Be(expectedType);
+ result.Confidence.Should().BeApproximately(0.9f, 0.001f);
+ }
+
+ [Fact]
+ public async Task ClassifyContent_UnknownType_FallsBackToVocabulary()
+ {
+ // Arrange — AI returns a type not in the switch arms
+ _mockAiService
+ .Setup(s => s.SendPrompt(It.IsAny()))
+ .ReturnsAsync(new ContentClassificationAiResponse
+ {
+ Type = "unknown_future_type",
+ Confidence = 0.6f,
+ Reasoning = "unrecognised",
+ Signals = new List()
+ });
+
+ using var scope = _serviceProvider.CreateScope();
+ var service = scope.ServiceProvider.GetRequiredService();
+
+ // Act
+ var result = await service.ClassifyContentAsync("some content sample", null);
+
+ // Assert
+ result.ContentType.Should().Be(ContentType.Vocabulary);
+ }
+}