chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,407 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
// TODO: Reimplement these as integration tests, #10464
|
||||
|
||||
#if DISABLED
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Common;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.VectorData;
|
||||
using Microsoft.SemanticKernel.Connectors.Sqlite;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace SemanticKernel.Connectors.Sqlite.UnitTests;
|
||||
|
||||
/// <summary>
|
||||
/// Unit tests for <see cref="SqliteCollection{TRecord}"/> class.
|
||||
/// </summary>
|
||||
public sealed class SqliteCollectionTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(1)]
|
||||
[InlineData(0)]
|
||||
public async Task CollectionExistsReturnsValidResultAsync(long tableCount)
|
||||
{
|
||||
// Arrange
|
||||
const string TableName = "CollectionExists";
|
||||
|
||||
using var fakeCommand = new FakeDbCommand(scalarResult: tableCount);
|
||||
using var fakeConnection = new FakeDBConnection(fakeCommand);
|
||||
|
||||
var sut = new SqliteVectorStoreRecordCollection<TestRecord<long>>(fakeConnection, TableName);
|
||||
|
||||
// Act
|
||||
var result = await sut.CollectionExistsAsync();
|
||||
|
||||
Assert.Equal(tableCount > 0, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateCollectionCallsExecuteNonQueryMethodAsync()
|
||||
{
|
||||
// Arrange
|
||||
const string TableName = "CreateCollection";
|
||||
|
||||
using var fakeCommand = new FakeDbCommand();
|
||||
using var fakeConnection = new FakeDBConnection(fakeCommand);
|
||||
|
||||
var sut = new SqliteVectorStoreRecordCollection<TestRecord<long>>(fakeConnection, TableName);
|
||||
|
||||
// Act
|
||||
await sut.CreateCollectionAsync();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(2, fakeCommand.ExecuteNonQueryCallCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task EnsureCollectionExistsCallsExecuteNonQueryMethodAsync()
|
||||
{
|
||||
// Arrange
|
||||
const string TableName = "EnsureCollectionExists";
|
||||
|
||||
using var fakeCommand = new FakeDbCommand();
|
||||
using var fakeConnection = new FakeDBConnection(fakeCommand);
|
||||
|
||||
var sut = new SqliteVectorStoreRecordCollection<TestRecord<long>>(fakeConnection, TableName);
|
||||
|
||||
// Act
|
||||
await sut.EnsureCollectionExistsAsync();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(2, fakeCommand.ExecuteNonQueryCallCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeleteCollectionCallsExecuteNonQueryMethodAsync()
|
||||
{
|
||||
// Arrange
|
||||
using var fakeCommandWithVectorProperty = new FakeDbCommand();
|
||||
using var fakeConnectionWithVectorProperty = new FakeDBConnection(fakeCommandWithVectorProperty);
|
||||
|
||||
using var fakeCommandWithoutVectorProperty = new FakeDbCommand();
|
||||
using var fakeConnectionWithoutVectorProperty = new FakeDBConnection(fakeCommandWithoutVectorProperty);
|
||||
|
||||
var collectionWithVectorProperty = new SqliteVectorStoreRecordCollection<TestRecord<long>>(fakeConnectionWithVectorProperty, "WithVectorProperty");
|
||||
var collectionWithoutVectorProperty = new SqliteVectorStoreRecordCollection<TestRecordWithoutVectorProperty<long>>(fakeConnectionWithoutVectorProperty, "WithoutVectorProperty");
|
||||
|
||||
// Act
|
||||
await collectionWithVectorProperty.DeleteCollectionAsync();
|
||||
await collectionWithoutVectorProperty.DeleteCollectionAsync();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(2, fakeCommandWithVectorProperty.ExecuteNonQueryCallCount);
|
||||
Assert.Equal(1, fakeCommandWithoutVectorProperty.ExecuteNonQueryCallCount);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true)]
|
||||
[InlineData(false)]
|
||||
public async Task VectorizedSearchReturnsRecordAsync(bool includeVectors)
|
||||
{
|
||||
// Arrange
|
||||
var expectedRecord = new TestRecord<long> { Key = 1, Data = "Test data", Vector = new ReadOnlyMemory<float>([1f, 2f, 3f, 4f]) };
|
||||
|
||||
var mockReader = new Mock<DbDataReader>();
|
||||
|
||||
SetupDbDataReaderGetBatch(mockReader, [expectedRecord]);
|
||||
|
||||
mockReader.Setup(l => l.GetOrdinal("distance")).Returns(3);
|
||||
mockReader.Setup(l => l.GetFieldValue<double>(3)).Returns(5.5);
|
||||
|
||||
using var fakeCommand = new FakeDbCommand(mockReader.Object);
|
||||
using var fakeConnection = new FakeDBConnection(fakeCommand);
|
||||
|
||||
var sut = new SqliteVectorStoreRecordCollection<TestRecord<long>>(fakeConnection, "VectorizedSearch");
|
||||
|
||||
// Act
|
||||
var result = await sut.VectorizedSearchAsync(expectedRecord.Vector, new() { IncludeVectors = includeVectors }).FirstOrDefaultAsync();
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
|
||||
Assert.Equal(5.5, result.Score);
|
||||
|
||||
AssertRecord(expectedRecord, result.Record, includeVectors);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true)]
|
||||
[InlineData(false)]
|
||||
public async Task GetReturnsValidRecordAsync(bool includeVectors)
|
||||
{
|
||||
// Arrange
|
||||
var expectedRecord = new TestRecord<long> { Key = 1, Data = "Test data", Vector = new ReadOnlyMemory<float>([1f, 2f, 3f, 4f]) };
|
||||
|
||||
var mockReader = new Mock<DbDataReader>();
|
||||
|
||||
SetupDbDataReaderGetBatch(mockReader, [expectedRecord]);
|
||||
|
||||
using var fakeCommand = new FakeDbCommand(mockReader.Object);
|
||||
using var fakeConnection = new FakeDBConnection(fakeCommand);
|
||||
|
||||
var sut = new SqliteVectorStoreRecordCollection<TestRecord<long>>(fakeConnection, "Get");
|
||||
|
||||
// Act
|
||||
var actualRecord = await sut.GetAsync(expectedRecord.Key, new() { IncludeVectors = includeVectors });
|
||||
|
||||
// Assert
|
||||
AssertRecord(expectedRecord, actualRecord, includeVectors);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true)]
|
||||
[InlineData(false)]
|
||||
public async Task GetBatchReturnsValidRecordsAsync(bool includeVectors)
|
||||
{
|
||||
// Arrange
|
||||
var expectedRecord1 = new TestRecord<long> { Key = 1, Data = "Test data", Vector = new ReadOnlyMemory<float>([1f, 2f, 3f, 4f]) };
|
||||
var expectedRecord2 = new TestRecord<long> { Key = 2, Data = "Test data", Vector = new ReadOnlyMemory<float>([1f, 2f, 3f, 4f]) };
|
||||
var expectedRecord3 = new TestRecord<long> { Key = 3, Data = "Test data", Vector = new ReadOnlyMemory<float>([1f, 2f, 3f, 4f]) };
|
||||
|
||||
var expectedRecords = new List<TestRecord<long>> { expectedRecord1, expectedRecord2, expectedRecord3 };
|
||||
|
||||
var mockReader = new Mock<DbDataReader>();
|
||||
|
||||
SetupDbDataReaderGetBatch(mockReader, expectedRecords);
|
||||
|
||||
using var fakeCommand = new FakeDbCommand(mockReader.Object);
|
||||
using var fakeConnection = new FakeDBConnection(fakeCommand);
|
||||
|
||||
var sut = new SqliteVectorStoreRecordCollection<TestRecord<long>>(fakeConnection, "GetBatch");
|
||||
|
||||
// Act
|
||||
var actualRecords = await sut
|
||||
.GetBatchAsync(expectedRecords.Select(l => l.Key), new() { IncludeVectors = includeVectors })
|
||||
.ToListAsync();
|
||||
|
||||
// Assert
|
||||
for (var i = 0; i < actualRecords.Count; i++)
|
||||
{
|
||||
AssertRecord(expectedRecords[i], actualRecords[i], includeVectors);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpsertReturnsKeyAsync()
|
||||
{
|
||||
// Arrange
|
||||
var expectedRecord = new TestRecord<long> { Key = 1, Data = "Test data", Vector = new ReadOnlyMemory<float>([1f, 2f, 3f, 4f]) };
|
||||
|
||||
var mockReader = new Mock<DbDataReader>();
|
||||
|
||||
SetupDbDataReaderUpsertBatch(mockReader, [expectedRecord.Key]);
|
||||
|
||||
using var fakeCommand = new FakeDbCommand(mockReader.Object);
|
||||
using var fakeConnection = new FakeDBConnection(fakeCommand);
|
||||
|
||||
var sut = new SqliteVectorStoreRecordCollection<TestRecord<long>>(fakeConnection, "Upsert");
|
||||
|
||||
// Act
|
||||
var actualKey = await sut.UpsertAsync(expectedRecord);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedRecord.Key, actualKey);
|
||||
|
||||
Assert.Equal(2, fakeCommand.ExecuteNonQueryCallCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpsertWithoutVectorPropertyReturnsKeyAsync()
|
||||
{
|
||||
// Arrange
|
||||
var expectedRecord = new TestRecordWithoutVectorProperty<long> { Key = 1, Data = "Test data" };
|
||||
|
||||
var mockReader = new Mock<DbDataReader>();
|
||||
|
||||
SetupDbDataReaderUpsertBatch(mockReader, [expectedRecord.Key]);
|
||||
|
||||
using var fakeCommand = new FakeDbCommand(mockReader.Object);
|
||||
using var fakeConnection = new FakeDBConnection(fakeCommand);
|
||||
|
||||
var sut = new SqliteVectorStoreRecordCollection<TestRecordWithoutVectorProperty<long>>(fakeConnection, "UpsertWithoutVectorProperty");
|
||||
|
||||
// Act
|
||||
var actualKey = await sut.UpsertAsync(expectedRecord);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedRecord.Key, actualKey);
|
||||
|
||||
Assert.Equal(0, fakeCommand.ExecuteNonQueryCallCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpsertBatchReturnsKeysAsync()
|
||||
{
|
||||
// Arrange
|
||||
var expectedRecord1 = new TestRecord<long> { Key = 1, Data = "Test data", Vector = new ReadOnlyMemory<float>([1f, 2f, 3f, 4f]) };
|
||||
var expectedRecord2 = new TestRecord<long> { Key = 2, Data = "Test data", Vector = new ReadOnlyMemory<float>([1f, 2f, 3f, 4f]) };
|
||||
var expectedRecord3 = new TestRecord<long> { Key = 3, Data = "Test data", Vector = new ReadOnlyMemory<float>([1f, 2f, 3f, 4f]) };
|
||||
|
||||
var expectedRecords = new List<TestRecord<long>> { expectedRecord1, expectedRecord2, expectedRecord3 };
|
||||
|
||||
var mockReader = new Mock<DbDataReader>();
|
||||
|
||||
SetupDbDataReaderUpsertBatch(mockReader, expectedRecords.Select(l => l.Key));
|
||||
|
||||
using var fakeCommand = new FakeDbCommand(mockReader.Object);
|
||||
using var fakeConnection = new FakeDBConnection(fakeCommand);
|
||||
|
||||
var sut = new SqliteVectorStoreRecordCollection<TestRecord<long>>(fakeConnection, "UpsertBatch");
|
||||
|
||||
// Act
|
||||
var actualKeys = await sut.UpsertBatchAsync(expectedRecords).ToListAsync();
|
||||
|
||||
// Assert
|
||||
Assert.Contains(expectedRecord1.Key, actualKeys);
|
||||
Assert.Contains(expectedRecord2.Key, actualKeys);
|
||||
Assert.Contains(expectedRecord3.Key, actualKeys);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeleteCallsExecuteNonQueryMethodAsync()
|
||||
{
|
||||
using var fakeCommandWithVectorProperty = new FakeDbCommand();
|
||||
using var fakeConnectionWithVectorProperty = new FakeDBConnection(fakeCommandWithVectorProperty);
|
||||
|
||||
using var fakeCommandWithoutVectorProperty = new FakeDbCommand();
|
||||
using var fakeConnectionWithoutVectorProperty = new FakeDBConnection(fakeCommandWithoutVectorProperty);
|
||||
|
||||
var collectionWithVectorProperty = new SqliteVectorStoreRecordCollection<TestRecord<long>>(fakeConnectionWithVectorProperty, "WithVectorProperty");
|
||||
var collectionWithoutVectorProperty = new SqliteVectorStoreRecordCollection<TestRecordWithoutVectorProperty<long>>(fakeConnectionWithoutVectorProperty, "WithoutVectorProperty");
|
||||
|
||||
// Act
|
||||
await collectionWithVectorProperty.DeleteAsync(key: 1);
|
||||
await collectionWithoutVectorProperty.DeleteAsync(key: 2);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(2, fakeCommandWithVectorProperty.ExecuteNonQueryCallCount);
|
||||
Assert.Equal(1, fakeCommandWithoutVectorProperty.ExecuteNonQueryCallCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeleteBatchCallsExecuteNonQueryMethodAsync()
|
||||
{
|
||||
using var fakeCommandWithVectorProperty = new FakeDbCommand();
|
||||
using var fakeConnectionWithVectorProperty = new FakeDBConnection(fakeCommandWithVectorProperty);
|
||||
|
||||
using var fakeCommandWithoutVectorProperty = new FakeDbCommand();
|
||||
using var fakeConnectionWithoutVectorProperty = new FakeDBConnection(fakeCommandWithoutVectorProperty);
|
||||
|
||||
var collectionWithVectorProperty = new SqliteVectorStoreRecordCollection<TestRecord<long>>(fakeConnectionWithVectorProperty, "WithVectorProperty");
|
||||
var collectionWithoutVectorProperty = new SqliteVectorStoreRecordCollection<TestRecordWithoutVectorProperty<long>>(fakeConnectionWithoutVectorProperty, "WithoutVectorProperty");
|
||||
|
||||
// Act
|
||||
await collectionWithVectorProperty.DeleteBatchAsync(keys: [1, 2, 3]);
|
||||
await collectionWithoutVectorProperty.DeleteBatchAsync(keys: [4, 5, 6]);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(2, fakeCommandWithVectorProperty.ExecuteNonQueryCallCount);
|
||||
Assert.Equal(1, fakeCommandWithoutVectorProperty.ExecuteNonQueryCallCount);
|
||||
}
|
||||
|
||||
#region private
|
||||
|
||||
private static void SetupDbDataReaderGetBatch<TKey>(Mock<DbDataReader> mockReader, List<TestRecord<TKey>> records)
|
||||
{
|
||||
var readSequence = mockReader.SetupSequence(l => l.ReadAsync(It.IsAny<CancellationToken>()));
|
||||
|
||||
var numericKeySequence = mockReader.SetupSequence(l => l.GetInt64(0));
|
||||
var stringKeySequence = mockReader.SetupSequence(l => l.GetString(0));
|
||||
|
||||
var dataSequence = mockReader.SetupSequence(l => l.GetString(1));
|
||||
var vectorSequence = mockReader.SetupSequence(l => l[2]);
|
||||
|
||||
mockReader.Setup(l => l.IsDBNull(It.IsAny<int>())).Returns(false);
|
||||
|
||||
mockReader.Setup(l => l.GetOrdinal(nameof(TestRecord<TKey>.Key))).Returns(0);
|
||||
mockReader.Setup(l => l.GetOrdinal(nameof(TestRecord<TKey>.Data))).Returns(1);
|
||||
mockReader.Setup(l => l.GetOrdinal(nameof(TestRecord<TKey>.Vector))).Returns(2);
|
||||
|
||||
foreach (var record in records)
|
||||
{
|
||||
var vectorBytes = SqliteVectorStoreRecordPropertyMapping.MapVectorForStorageModel(record.Vector);
|
||||
|
||||
if (record.Key is long numericKey)
|
||||
{
|
||||
numericKeySequence.Returns(numericKey);
|
||||
}
|
||||
|
||||
if (record.Key is string stringKey)
|
||||
{
|
||||
stringKeySequence.Returns(stringKey);
|
||||
}
|
||||
|
||||
dataSequence.Returns(record.Data!);
|
||||
vectorSequence.Returns(vectorBytes);
|
||||
|
||||
readSequence.ReturnsAsync(true);
|
||||
}
|
||||
|
||||
readSequence.ReturnsAsync(false);
|
||||
}
|
||||
|
||||
private static void SetupDbDataReaderUpsertBatch<TKey>(Mock<DbDataReader> mockReader, IEnumerable<TKey> keys)
|
||||
{
|
||||
var readSequence = mockReader.SetupSequence(l => l.ReadAsync(It.IsAny<CancellationToken>()));
|
||||
var keySequence = mockReader.SetupSequence(l => l.GetFieldValue<TKey>(0));
|
||||
|
||||
foreach (var key in keys)
|
||||
{
|
||||
keySequence.Returns(key);
|
||||
readSequence.ReturnsAsync(true);
|
||||
}
|
||||
|
||||
readSequence.ReturnsAsync(false);
|
||||
}
|
||||
|
||||
private static void AssertRecord<TKey>(TestRecord<TKey> expectedRecord, TestRecord<TKey>? actualRecord, bool includeVectors)
|
||||
{
|
||||
Assert.NotNull(actualRecord);
|
||||
|
||||
Assert.Equal(expectedRecord.Key, actualRecord.Key);
|
||||
Assert.Equal(expectedRecord.Data, actualRecord.Data);
|
||||
|
||||
if (includeVectors)
|
||||
{
|
||||
Assert.NotNull(actualRecord.Vector);
|
||||
Assert.Equal(expectedRecord.Vector!.Value.ToArray(), actualRecord.Vector.Value.Span.ToArray());
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Null(actualRecord.Vector);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning disable CA1812
|
||||
private sealed class TestRecord<TKey>
|
||||
{
|
||||
[VectorStoreRecordKey]
|
||||
public TKey? Key { get; set; }
|
||||
|
||||
[VectorStoreRecordData]
|
||||
public string? Data { get; set; }
|
||||
|
||||
[VectorStoreRecordVector(Dimensions: 4, DistanceFunction: DistanceFunction.CosineDistance)]
|
||||
public ReadOnlyMemory<float>? Vector { get; set; }
|
||||
}
|
||||
|
||||
private sealed class TestRecordWithoutVectorProperty<TKey>
|
||||
{
|
||||
[VectorStoreRecordKey]
|
||||
public TKey? Key { get; set; }
|
||||
|
||||
[VectorStoreRecordData]
|
||||
public string? Data { get; set; }
|
||||
}
|
||||
#pragma warning restore CA1812
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,387 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Data.Sqlite;
|
||||
using Microsoft.Extensions.VectorData;
|
||||
using Microsoft.Extensions.VectorData.ProviderServices;
|
||||
using Microsoft.SemanticKernel.Connectors.SqliteVec;
|
||||
using Xunit;
|
||||
|
||||
namespace SemanticKernel.Connectors.SqliteVec.UnitTests;
|
||||
|
||||
/// <summary>
|
||||
/// Unit tests for <see cref="SqliteCommandBuilder"/> class.
|
||||
/// </summary>
|
||||
public sealed class SqliteCommandBuilderTests : IDisposable
|
||||
{
|
||||
private readonly SqliteCommand _command;
|
||||
private readonly SqliteConnection _connection;
|
||||
|
||||
public SqliteCommandBuilderTests()
|
||||
{
|
||||
this._command = new() { Connection = this._connection };
|
||||
this._connection = new();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ItBuildsTableCountCommand()
|
||||
{
|
||||
// Arrange
|
||||
const string TableName = "TestTable";
|
||||
|
||||
// Act
|
||||
var command = SqliteCommandBuilder.BuildTableCountCommand(this._connection, TableName);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("SELECT count(*) FROM sqlite_master WHERE type='table' AND name=@tableName;", command.CommandText);
|
||||
Assert.Equal("@tableName", command.Parameters[0].ParameterName);
|
||||
Assert.Equal(TableName, command.Parameters[0].Value);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true)]
|
||||
[InlineData(false)]
|
||||
public void ItBuildsCreateTableCommand(bool ifNotExists)
|
||||
{
|
||||
// Arrange
|
||||
const string TableName = "TestTable";
|
||||
|
||||
var columns = new List<SqliteColumn>
|
||||
{
|
||||
new("Column1", "Type1", isPrimary: true),
|
||||
new("Column2", "Type2", isPrimary: false) { IsNullable = true, Configuration = new() { ["distance_metric"] = "l2" } },
|
||||
new("Column3", "Type3", isPrimary: false) { IsNullable = false },
|
||||
new("Column4", "Type4", isPrimary: false) { IsNullable = true },
|
||||
};
|
||||
|
||||
// Act
|
||||
var command = SqliteCommandBuilder.BuildCreateTableCommand(this._connection, TableName, columns, ifNotExists);
|
||||
|
||||
// Assert
|
||||
Assert.Contains("CREATE TABLE", command.CommandText);
|
||||
Assert.Contains(TableName, command.CommandText);
|
||||
|
||||
Assert.Equal(ifNotExists, command.CommandText.Contains("IF NOT EXISTS"));
|
||||
|
||||
Assert.Contains("\"Column1\" Type1 PRIMARY KEY", command.CommandText);
|
||||
Assert.Contains("\"Column2\" Type2 distance_metric=l2", command.CommandText);
|
||||
Assert.Contains("\"Column3\" Type3 NOT NULL", command.CommandText);
|
||||
Assert.Contains("\"Column4\" Type4", command.CommandText);
|
||||
Assert.DoesNotContain("\"Column4\" Type4 NOT NULL", command.CommandText);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true)]
|
||||
[InlineData(false)]
|
||||
public void ItBuildsCreateVirtualTableCommand(bool ifNotExists)
|
||||
{
|
||||
// Arrange
|
||||
const string TableName = "TestTable";
|
||||
|
||||
var columns = new List<SqliteColumn>
|
||||
{
|
||||
new("Column1", "Type1", isPrimary: true),
|
||||
new("Column2", "Type2", isPrimary: false) { IsNullable = true, Configuration = new() { ["distance_metric"] = "l2" } },
|
||||
};
|
||||
|
||||
// Act
|
||||
var command = SqliteCommandBuilder.BuildCreateVirtualTableCommand(this._connection, TableName, columns, ifNotExists);
|
||||
|
||||
// Assert
|
||||
Assert.Contains("CREATE VIRTUAL TABLE", command.CommandText);
|
||||
Assert.Contains(TableName, command.CommandText);
|
||||
Assert.Contains("USING vec0", command.CommandText);
|
||||
|
||||
Assert.Equal(ifNotExists, command.CommandText.Contains("IF NOT EXISTS"));
|
||||
|
||||
Assert.Contains("Column1 Type1 PRIMARY KEY", command.CommandText);
|
||||
Assert.Contains("Column2 Type2 distance_metric=l2", command.CommandText);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ItBuildsDropTableCommand()
|
||||
{
|
||||
// Arrange
|
||||
const string TableName = "TestTable";
|
||||
|
||||
// Act
|
||||
var command = SqliteCommandBuilder.BuildDropTableCommand(this._connection, TableName);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("DROP TABLE IF EXISTS \"TestTable\";", command.CommandText);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true)]
|
||||
[InlineData(false)]
|
||||
public void ItBuildsInsertCommand_without_autogenerated_key(bool replaceIfExists)
|
||||
{
|
||||
// Arrange
|
||||
const string TableName = "TestTable";
|
||||
|
||||
var model = BuildModel(
|
||||
[
|
||||
new VectorStoreKeyProperty("Id", typeof(int)),
|
||||
new VectorStoreDataProperty("Name", typeof(string)),
|
||||
new VectorStoreDataProperty("Age", typeof(string)),
|
||||
new VectorStoreDataProperty("Address", typeof(string)),
|
||||
]);
|
||||
|
||||
var records = new List<Dictionary<string, object?>>
|
||||
{
|
||||
new() { ["Id"] = 1, ["Name"] = "NameValue1", ["Age"] = "AgeValue1", ["Address"] = "AddressValue1" },
|
||||
new() { ["Id"] = 2, ["Name"] = "NameValue2", ["Age"] = "AgeValue2", ["Address"] = "AddressValue2" },
|
||||
};
|
||||
|
||||
// Act
|
||||
var command = SqliteCommandBuilder.BuildInsertCommand(
|
||||
this._connection,
|
||||
TableName,
|
||||
model,
|
||||
records,
|
||||
generatedEmbeddings: null,
|
||||
data: true,
|
||||
replaceIfExists: replaceIfExists);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(replaceIfExists, command.CommandText.Contains("OR REPLACE"));
|
||||
|
||||
Assert.Contains($"INTO \"{TableName}\" (\"Id\", \"Name\", \"Age\", \"Address\")", command.CommandText);
|
||||
Assert.Contains("VALUES (@Id0, @Name0, @Age0, @Address0)", command.CommandText);
|
||||
Assert.Contains("VALUES (@Id1, @Name1, @Age1, @Address1)", command.CommandText);
|
||||
Assert.DoesNotContain("RETURNING", command.CommandText);
|
||||
|
||||
Assert.Equal("@Id0", command.Parameters[0].ParameterName);
|
||||
Assert.Equal(1, command.Parameters[0].Value);
|
||||
|
||||
Assert.Equal("@Name0", command.Parameters[1].ParameterName);
|
||||
Assert.Equal("NameValue1", command.Parameters[1].Value);
|
||||
|
||||
Assert.Equal("@Age0", command.Parameters[2].ParameterName);
|
||||
Assert.Equal("AgeValue1", command.Parameters[2].Value);
|
||||
|
||||
Assert.Equal("@Address0", command.Parameters[3].ParameterName);
|
||||
Assert.Equal("AddressValue1", command.Parameters[3].Value);
|
||||
|
||||
Assert.Equal("@Id1", command.Parameters[4].ParameterName);
|
||||
Assert.Equal(2, command.Parameters[4].Value);
|
||||
|
||||
Assert.Equal("@Name1", command.Parameters[5].ParameterName);
|
||||
Assert.Equal("NameValue2", command.Parameters[5].Value);
|
||||
|
||||
Assert.Equal("@Age1", command.Parameters[6].ParameterName);
|
||||
Assert.Equal("AgeValue2", command.Parameters[6].Value);
|
||||
|
||||
Assert.Equal("@Address1", command.Parameters[7].ParameterName);
|
||||
Assert.Equal("AddressValue2", command.Parameters[7].Value);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true)]
|
||||
[InlineData(false)]
|
||||
public void ItBuildsInsertCommand_with_autogenerated_key(bool replaceIfExists)
|
||||
{
|
||||
// Arrange
|
||||
const string TableName = "TestTable";
|
||||
|
||||
var model = BuildModel(
|
||||
[
|
||||
new VectorStoreKeyProperty("Id", typeof(int)),
|
||||
new VectorStoreDataProperty("Name", typeof(string)),
|
||||
new VectorStoreDataProperty("Age", typeof(string)),
|
||||
new VectorStoreDataProperty("Address", typeof(string)),
|
||||
]);
|
||||
|
||||
var records = new List<Dictionary<string, object?>>
|
||||
{
|
||||
new() { ["Id"] = default(int), ["Name"] = "NameValue1", ["Age"] = "AgeValue1", ["Address"] = "AddressValue1" },
|
||||
new() { ["Id"] = default(int), ["Name"] = "NameValue2", ["Age"] = "AgeValue2", ["Address"] = "AddressValue2" },
|
||||
};
|
||||
|
||||
// Act
|
||||
var command = SqliteCommandBuilder.BuildInsertCommand(
|
||||
this._connection,
|
||||
TableName,
|
||||
model,
|
||||
records,
|
||||
generatedEmbeddings: null,
|
||||
data: true,
|
||||
replaceIfExists: replaceIfExists);
|
||||
|
||||
// Assert
|
||||
Assert.DoesNotContain("OR REPLACE", command.CommandText);
|
||||
|
||||
Assert.Contains($"INTO \"{TableName}\" (\"Name\", \"Age\", \"Address\")", command.CommandText);
|
||||
Assert.Contains("VALUES (@Name0, @Age0, @Address0)", command.CommandText);
|
||||
Assert.Contains("VALUES (@Name1, @Age1, @Address1)", command.CommandText);
|
||||
Assert.Contains("RETURNING \"Id\"", command.CommandText);
|
||||
|
||||
Assert.Equal("@Name0", command.Parameters[0].ParameterName);
|
||||
Assert.Equal("NameValue1", command.Parameters[0].Value);
|
||||
|
||||
Assert.Equal("@Name1", command.Parameters[3].ParameterName);
|
||||
Assert.Equal("NameValue2", command.Parameters[3].Value);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(null)]
|
||||
[InlineData("")]
|
||||
[InlineData("Age")]
|
||||
public void ItBuildsSelectCommand(string? orderByPropertyName)
|
||||
{
|
||||
// Arrange
|
||||
const string TableName = "TestTable";
|
||||
|
||||
var model = BuildModel(
|
||||
[
|
||||
new VectorStoreKeyProperty("Id", typeof(string)),
|
||||
new VectorStoreDataProperty("Name", typeof(string)),
|
||||
new VectorStoreDataProperty("Age", typeof(string)),
|
||||
new VectorStoreDataProperty("Address", typeof(string)),
|
||||
]);
|
||||
var conditions = new List<SqliteWhereCondition>
|
||||
{
|
||||
new SqliteWhereEqualsCondition("Name", "NameValue"),
|
||||
new SqliteWhereInCondition("Age", [10, 20, 30]),
|
||||
};
|
||||
FilteredRecordRetrievalOptions<Dictionary<string, object?>> filterOptions = new();
|
||||
if (!string.IsNullOrWhiteSpace(orderByPropertyName))
|
||||
{
|
||||
filterOptions.OrderBy = orderBy => orderBy.Ascending(record => record[orderByPropertyName]);
|
||||
}
|
||||
|
||||
// Act
|
||||
var command = SqliteCommandBuilder.BuildSelectDataCommand<Dictionary<string, object?>>(this._connection, TableName, model, conditions, filterOptions);
|
||||
|
||||
// Assert
|
||||
Assert.Contains("SELECT \"Id\",\"Name\",\"Age\",\"Address\"", command.CommandText);
|
||||
Assert.Contains($"FROM \"{TableName}\"", command.CommandText);
|
||||
|
||||
Assert.Contains("\"Name\" = @Name0", command.CommandText);
|
||||
Assert.Contains("\"Age\" IN (@Age0, @Age1, @Age2)", command.CommandText);
|
||||
|
||||
Assert.Equal(!string.IsNullOrWhiteSpace(orderByPropertyName), command.CommandText.Contains($"ORDER BY \"{orderByPropertyName}\""));
|
||||
|
||||
Assert.Equal("@Name0", command.Parameters[0].ParameterName);
|
||||
Assert.Equal("NameValue", command.Parameters[0].Value);
|
||||
|
||||
Assert.Equal("@Age0", command.Parameters[1].ParameterName);
|
||||
Assert.Equal(10, command.Parameters[1].Value);
|
||||
|
||||
Assert.Equal("@Age1", command.Parameters[2].ParameterName);
|
||||
Assert.Equal(20, command.Parameters[2].Value);
|
||||
|
||||
Assert.Equal("@Age2", command.Parameters[3].ParameterName);
|
||||
Assert.Equal(30, command.Parameters[3].Value);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(null)]
|
||||
[InlineData("")]
|
||||
[InlineData("Age")]
|
||||
public void ItBuildsSelectInnerJoinCommand(string? orderByPropertyName)
|
||||
{
|
||||
// Arrange
|
||||
const string DataTable = "DataTable";
|
||||
const string VectorTable = "VectorTable";
|
||||
const string JoinColumnName = "Id";
|
||||
|
||||
var model = BuildModel(
|
||||
[
|
||||
new VectorStoreKeyProperty("Id", typeof(string)),
|
||||
new VectorStoreDataProperty("Name", typeof(string)),
|
||||
new VectorStoreVectorProperty("Age", typeof(ReadOnlyMemory<float>), 10),
|
||||
new VectorStoreVectorProperty("Address", typeof(ReadOnlyMemory<float>), 10),
|
||||
]);
|
||||
|
||||
var conditions = new List<SqliteWhereCondition>
|
||||
{
|
||||
new SqliteWhereEqualsCondition("Name", "NameValue"),
|
||||
new SqliteWhereInCondition("Age", [10, 20, 30]),
|
||||
};
|
||||
FilteredRecordRetrievalOptions<Dictionary<string, object?>> filterOptions = new();
|
||||
if (!string.IsNullOrWhiteSpace(orderByPropertyName))
|
||||
{
|
||||
filterOptions.OrderBy = orderBy => orderBy.Ascending(record => record[orderByPropertyName]);
|
||||
}
|
||||
|
||||
// Act
|
||||
var command = SqliteCommandBuilder.BuildSelectInnerJoinCommand(
|
||||
this._connection,
|
||||
VectorTable,
|
||||
DataTable,
|
||||
JoinColumnName,
|
||||
model,
|
||||
conditions,
|
||||
true,
|
||||
filterOptions);
|
||||
|
||||
// Assert
|
||||
Assert.Contains("SELECT \"DataTable\".\"Id\",\"DataTable\".\"Name\",\"VectorTable\".\"Age\",\"VectorTable\".\"Address\"", command.CommandText);
|
||||
Assert.Contains("FROM \"VectorTable\"", command.CommandText);
|
||||
|
||||
Assert.Contains("INNER JOIN \"DataTable\" ON \"VectorTable\".\"Id\" = \"DataTable\".\"Id\"", command.CommandText);
|
||||
|
||||
Assert.Contains("\"Name\" = @Name0", command.CommandText);
|
||||
Assert.Contains("\"Age\" IN (@Age0, @Age1, @Age2)", command.CommandText);
|
||||
|
||||
Assert.Equal(!string.IsNullOrWhiteSpace(orderByPropertyName), command.CommandText.Contains($"ORDER BY \"DataTable\".\"{orderByPropertyName}\""));
|
||||
|
||||
Assert.Equal("@Name0", command.Parameters[0].ParameterName);
|
||||
Assert.Equal("NameValue", command.Parameters[0].Value);
|
||||
|
||||
Assert.Equal("@Age0", command.Parameters[1].ParameterName);
|
||||
Assert.Equal(10, command.Parameters[1].Value);
|
||||
|
||||
Assert.Equal("@Age1", command.Parameters[2].ParameterName);
|
||||
Assert.Equal(20, command.Parameters[2].Value);
|
||||
|
||||
Assert.Equal("@Age2", command.Parameters[3].ParameterName);
|
||||
Assert.Equal(30, command.Parameters[3].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ItBuildsDeleteCommand()
|
||||
{
|
||||
// Arrange
|
||||
const string TableName = "TestTable";
|
||||
|
||||
var conditions = new List<SqliteWhereCondition>
|
||||
{
|
||||
new SqliteWhereEqualsCondition("Name", "NameValue"),
|
||||
new SqliteWhereInCondition("Age", [10, 20, 30]),
|
||||
};
|
||||
|
||||
// Act
|
||||
var command = SqliteCommandBuilder.BuildDeleteCommand(this._connection, TableName, conditions);
|
||||
|
||||
// Assert
|
||||
Assert.Contains("DELETE FROM \"TestTable\"", command.CommandText);
|
||||
|
||||
Assert.Contains("\"Name\" = @Name0", command.CommandText);
|
||||
Assert.Contains("\"Age\" IN (@Age0, @Age1, @Age2)", command.CommandText);
|
||||
|
||||
Assert.Equal("@Name0", command.Parameters[0].ParameterName);
|
||||
Assert.Equal("NameValue", command.Parameters[0].Value);
|
||||
|
||||
Assert.Equal("@Age0", command.Parameters[1].ParameterName);
|
||||
Assert.Equal(10, command.Parameters[1].Value);
|
||||
|
||||
Assert.Equal("@Age1", command.Parameters[2].ParameterName);
|
||||
Assert.Equal(20, command.Parameters[2].Value);
|
||||
|
||||
Assert.Equal("@Age2", command.Parameters[3].ParameterName);
|
||||
Assert.Equal(30, command.Parameters[3].Value);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
this._command.Dispose();
|
||||
this._connection.Dispose();
|
||||
}
|
||||
|
||||
private static CollectionModel BuildModel(List<VectorStoreProperty> properties)
|
||||
=> new SqliteModelBuilder()
|
||||
.BuildDynamic(new() { Properties = properties }, defaultEmbeddingGenerator: null);
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using Microsoft.SemanticKernel.Connectors.SqliteVec;
|
||||
using Xunit;
|
||||
|
||||
namespace SemanticKernel.Connectors.SqliteVec.UnitTests;
|
||||
|
||||
/// <summary>
|
||||
/// Unit tests for SQLite condition classes.
|
||||
/// </summary>
|
||||
public sealed class SqliteConditionsTests
|
||||
{
|
||||
[Fact]
|
||||
public void SqliteWhereEqualsConditionWithoutParameterNamesThrowsException()
|
||||
{
|
||||
// Arrange
|
||||
var condition = new SqliteWhereEqualsCondition("Name", "Value");
|
||||
|
||||
// Act & Assert
|
||||
Assert.Throws<ArgumentException>(() => condition.BuildQuery([]));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(null, "\"Name\" = @Name0")]
|
||||
[InlineData("", "\"Name\" = @Name0")]
|
||||
[InlineData("TableName", "\"TableName\".\"Name\" = @Name0")]
|
||||
public void SqliteWhereEqualsConditionBuildsValidQuery(string? tableName, string expectedQuery)
|
||||
{
|
||||
// Arrange
|
||||
var condition = new SqliteWhereEqualsCondition("Name", "Value") { TableName = tableName };
|
||||
|
||||
// Act
|
||||
var query = condition.BuildQuery(["@Name0"]);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedQuery, query);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SqliteWhereInConditionWithoutParameterNamesThrowsException()
|
||||
{
|
||||
// Arrange
|
||||
var condition = new SqliteWhereInCondition("Name", ["Value1", "Value2"]);
|
||||
|
||||
// Act & Assert
|
||||
Assert.Throws<ArgumentException>(() => condition.BuildQuery([]));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(null, "\"Name\" IN (@Name0, @Name1)")]
|
||||
[InlineData("", "\"Name\" IN (@Name0, @Name1)")]
|
||||
[InlineData("TableName", "\"TableName\".\"Name\" IN (@Name0, @Name1)")]
|
||||
public void SqliteWhereInConditionBuildsValidQuery(string? tableName, string expectedQuery)
|
||||
{
|
||||
// Arrange
|
||||
var condition = new SqliteWhereInCondition("Name", ["Value1", "Value2"]) { TableName = tableName };
|
||||
|
||||
// Act
|
||||
var query = condition.BuildQuery(["@Name0", "@Name1"]);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedQuery, query);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SqliteWhereMatchConditionWithoutParameterNamesThrowsException()
|
||||
{
|
||||
// Arrange
|
||||
var condition = new SqliteWhereMatchCondition("Name", "Value");
|
||||
|
||||
// Act & Assert
|
||||
Assert.Throws<ArgumentException>(() => condition.BuildQuery([]));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(null, "\"Name\" MATCH @Name0")]
|
||||
[InlineData("", "\"Name\" MATCH @Name0")]
|
||||
[InlineData("TableName", "\"TableName\".\"Name\" MATCH @Name0")]
|
||||
public void SqliteWhereMatchConditionBuildsValidQuery(string? tableName, string expectedQuery)
|
||||
{
|
||||
// Arrange
|
||||
var condition = new SqliteWhereMatchCondition("Name", "Value") { TableName = tableName };
|
||||
|
||||
// Act
|
||||
var query = condition.BuildQuery(["@Name0"]);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedQuery, query);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using Microsoft.Extensions.VectorData;
|
||||
|
||||
namespace SemanticKernel.Connectors.SqliteVec.UnitTests;
|
||||
|
||||
public class SqliteHotel<TKey>()
|
||||
{
|
||||
/// <summary>The key of the record.</summary>
|
||||
[VectorStoreKey]
|
||||
public TKey? HotelId { get; init; }
|
||||
|
||||
/// <summary>A string metadata field.</summary>
|
||||
[VectorStoreData(IsIndexed = true)]
|
||||
public string? HotelName { get; set; }
|
||||
|
||||
/// <summary>An int metadata field.</summary>
|
||||
[VectorStoreData]
|
||||
public int HotelCode { get; set; }
|
||||
|
||||
/// <summary>A float metadata field.</summary>
|
||||
[VectorStoreData]
|
||||
public float? HotelRating { get; set; }
|
||||
|
||||
/// <summary>A bool metadata field.</summary>
|
||||
[VectorStoreData(StorageName = "parking_is_included")]
|
||||
public bool ParkingIncluded { get; set; }
|
||||
|
||||
/// <summary>A data field.</summary>
|
||||
[VectorStoreData]
|
||||
public string? Description { get; set; }
|
||||
|
||||
/// <summary>A vector field.</summary>
|
||||
[VectorStoreVector(Dimensions: 4, DistanceFunction = DistanceFunction.EuclideanDistance)]
|
||||
public ReadOnlyMemory<float>? DescriptionEmbedding { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Extensions.VectorData;
|
||||
using Microsoft.Extensions.VectorData.ProviderServices;
|
||||
using Microsoft.SemanticKernel.Connectors.SqliteVec;
|
||||
using Xunit;
|
||||
|
||||
namespace SemanticKernel.Connectors.SqliteVec.UnitTests;
|
||||
|
||||
/// <summary>
|
||||
/// Unit tests for <see cref="SqlitePropertyMapping"/> class.
|
||||
/// </summary>
|
||||
public sealed class SqlitePropertyMappingTests
|
||||
{
|
||||
[Fact]
|
||||
public void MapVectorForStorageModelReturnsByteArray()
|
||||
{
|
||||
// Arrange
|
||||
var vector = new ReadOnlyMemory<float>([1.1f, 2.2f, 3.3f, 4.4f]);
|
||||
|
||||
// Act
|
||||
var storageModelVector = SqlitePropertyMapping.MapVectorForStorageModel(vector);
|
||||
|
||||
// Assert
|
||||
Assert.IsType<byte[]>(storageModelVector);
|
||||
Assert.True(storageModelVector.Length > 0);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true)]
|
||||
[InlineData(false)]
|
||||
public void GetColumnsReturnsCollectionOfColumns(bool data)
|
||||
{
|
||||
// Arrange
|
||||
var properties = new List<PropertyModel>()
|
||||
{
|
||||
new KeyPropertyModel("Key", typeof(string)) { StorageName = "Key" },
|
||||
new DataPropertyModel("Data", typeof(int)) { StorageName = "my_data", IsIndexed = true },
|
||||
new DataPropertyModel("NullableData", typeof(int?)) { StorageName = "nullable_data" },
|
||||
new DataPropertyModel("StringData", typeof(string)) { StorageName = "string_data" },
|
||||
new VectorPropertyModel("Vector", typeof(ReadOnlyMemory<float>))
|
||||
{
|
||||
Dimensions = 4,
|
||||
DistanceFunction = DistanceFunction.ManhattanDistance,
|
||||
StorageName = "Vector"
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
var columns = SqlitePropertyMapping.GetColumns(properties, data: data);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("Key", columns[0].Name);
|
||||
Assert.Equal("TEXT", columns[0].Type);
|
||||
Assert.True(columns[0].IsPrimary);
|
||||
Assert.Null(columns[0].Configuration);
|
||||
Assert.False(columns[0].HasIndex);
|
||||
// string key is a reference type, so nullable
|
||||
Assert.True(columns[0].IsNullable);
|
||||
|
||||
if (data)
|
||||
{
|
||||
Assert.Equal("my_data", columns[1].Name);
|
||||
Assert.Equal("INTEGER", columns[1].Type);
|
||||
Assert.False(columns[1].IsPrimary);
|
||||
Assert.Null(columns[1].Configuration);
|
||||
Assert.True(columns[1].HasIndex);
|
||||
// int is a non-nullable value type
|
||||
Assert.False(columns[1].IsNullable);
|
||||
|
||||
Assert.Equal("nullable_data", columns[2].Name);
|
||||
Assert.Equal("INTEGER", columns[2].Type);
|
||||
Assert.False(columns[2].IsPrimary);
|
||||
// int? is a nullable value type
|
||||
Assert.True(columns[2].IsNullable);
|
||||
|
||||
Assert.Equal("string_data", columns[3].Name);
|
||||
Assert.Equal("TEXT", columns[3].Type);
|
||||
// string is a reference type, so nullable
|
||||
Assert.True(columns[3].IsNullable);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Equal("Vector", columns[1].Name);
|
||||
Assert.Equal("FLOAT[4]", columns[1].Type);
|
||||
Assert.False(columns[1].IsPrimary);
|
||||
Assert.NotNull(columns[1].Configuration);
|
||||
Assert.False(columns[1].HasIndex);
|
||||
Assert.Equal("l1", columns[1].Configuration!["distance_metric"]);
|
||||
// ReadOnlyMemory<float> is a non-nullable value type
|
||||
Assert.False(columns[1].IsNullable);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<AssemblyName>SemanticKernel.Connectors.SqliteVec.UnitTests</AssemblyName>
|
||||
<RootNamespace>SemanticKernel.Connectors.SqliteVec.UnitTests</RootNamespace>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<IsTestProject>true</IsTestProject>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>disable</ImplicitUsings>
|
||||
<IsPackable>false</IsPackable>
|
||||
<NoWarn>$(NoWarn);SKEXP0001,VSTHRD111,CA2007,CS1591</NoWarn>
|
||||
<NoWarn>$(NoWarn);MEVD9001</NoWarn> <!-- Experimental MEVD connector-facing APIs -->
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" />
|
||||
<PackageReference Include="Moq" />
|
||||
<PackageReference Include="xunit" />
|
||||
<PackageReference Include="xunit.runner.visualstudio">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="coverlet.collector">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\src\VectorData\SqliteVec\SqliteVec.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,80 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
// TODO: Reimplement these as integration tests, #10464
|
||||
|
||||
#if DISABLED
|
||||
|
||||
using System;
|
||||
using System.Data.Common;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Data.Sqlite;
|
||||
using Microsoft.Extensions.VectorData;
|
||||
using Microsoft.SemanticKernel.Connectors.Sqlite;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace SemanticKernel.Connectors.Sqlite.UnitTests;
|
||||
|
||||
/// <summary>
|
||||
/// Unit tests for <see cref="SqliteVectorStore"/> class.
|
||||
/// </summary>
|
||||
public sealed class SqliteVectorStoreTests
|
||||
{
|
||||
[Fact]
|
||||
public void GetCollectionWithNotSupportedKeyThrowsException()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new SqliteVectorStore(Mock.Of<SqliteConnection>());
|
||||
|
||||
// Act & Assert
|
||||
Assert.Throws<NotSupportedException>(() => sut.GetCollection<int, SqliteHotel<int>>("collection"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetCollectionWithSupportedKeyReturnsCollection()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new SqliteVectorStore(Mock.Of<SqliteConnection>());
|
||||
|
||||
// Act
|
||||
var collectionWithNumericKey = sut.GetCollection<long, SqliteHotel<long>>("collection1");
|
||||
var collectionWithStringKey = sut.GetCollection<string, SqliteHotel<string>>("collection2");
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(collectionWithNumericKey);
|
||||
Assert.NotNull(collectionWithStringKey);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ListCollectionNamesReturnsCollectionNamesAsync()
|
||||
{
|
||||
// Arrange
|
||||
var mockReader = new Mock<DbDataReader>();
|
||||
mockReader
|
||||
.SetupSequence(l => l.ReadAsync(It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(true)
|
||||
.ReturnsAsync(true)
|
||||
.ReturnsAsync(false);
|
||||
|
||||
mockReader
|
||||
.SetupSequence(l => l.GetString(It.IsAny<int>()))
|
||||
.Returns("collection1")
|
||||
.Returns("collection2");
|
||||
|
||||
using var fakeCommand = new FakeDbCommand(mockReader.Object);
|
||||
using var fakeConnection = new FakeDBConnection(fakeCommand);
|
||||
|
||||
var sut = new SqliteVectorStore(fakeConnection);
|
||||
|
||||
// Act
|
||||
var collections = await sut.ListCollectionNamesAsync().ToListAsync();
|
||||
|
||||
// Assert
|
||||
Assert.Contains("collection1", collections);
|
||||
Assert.Contains("collection2", collections);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user