blob: e10e12dc385d4d9f02df96edc7316cf822a346e5 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace Apache.Ignite.Tests.Table;
using System;
using System.ComponentModel.DataAnnotations.Schema;
using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks;
using Ignite.Table;
using NUnit.Framework;
/// <summary>
/// Tests schema validation.
/// <para>Client checks unmapped columns.</para>
/// <para>Server checks missing columns.</para>
/// </summary>
public class SchemaValidationTest : IgniteTestsBase
{
private const string TableNameRequiredVal = nameof(SchemaValidationTest);
private ITable TableRequiredVal { get; set; } = null!;
[OneTimeSetUp]
public async Task CreateTable()
{
await Client.Sql.ExecuteAsync(null, $"CREATE TABLE {TableNameRequiredVal} (KEY BIGINT PRIMARY KEY, VAL VARCHAR NOT NULL)");
TableRequiredVal = (await Client.Tables.GetTableAsync(TableNameRequiredVal))!;
}
[OneTimeTearDown]
public async Task DropTable() => await Client.Sql.ExecuteAsync(null, $"DROP TABLE {TableNameRequiredVal}");
[Test]
public void TestUnmappedTupleFields()
{
var igniteTuple = new IgniteTuple
{
[KeyCol] = 1L,
[ValCol] = "v",
["foo"] = "abc",
["bar"] = null
};
var ex = Assert.ThrowsAsync<ArgumentException>(async () => await TupleView.UpsertAsync(null, igniteTuple));
Assert.AreEqual("Tuple doesn't match schema: schemaVersion=1, extraColumns=FOO, BAR", ex!.Message);
}
[Test]
public void TestKvUnmappedKeyTupleFields()
{
var keyTuple = new IgniteTuple
{
[KeyCol] = 1L,
["bar"] = null
};
var valTuple = new IgniteTuple
{
[ValCol] = "v"
};
var kvView = Table.KeyValueBinaryView;
var ex = Assert.ThrowsAsync<ArgumentException>(async () => await kvView.PutAsync(null, keyTuple, valTuple));
Assert.AreEqual("Tuple pair doesn't match schema: schemaVersion=1, extraColumns=BAR", ex!.Message);
}
[Test]
public void TestKvUnmappedValTupleFields()
{
var keyTuple = new IgniteTuple
{
[KeyCol] = 1L
};
var valTuple = new IgniteTuple
{
[ValCol] = "v",
["baz"] = 0
};
var kvView = Table.KeyValueBinaryView;
var ex = Assert.ThrowsAsync<ArgumentException>(async () => await kvView.PutAsync(null, keyTuple, valTuple));
Assert.AreEqual("Tuple pair doesn't match schema: schemaVersion=1, extraColumns=BAZ", ex!.Message);
}
[Test]
public void TestMissingKeyTupleFields()
{
var igniteTuple = new IgniteTuple
{
[ValCol] = "v"
};
var ex = Assert.ThrowsAsync<MarshallerException>(async () => await TupleView.UpsertAsync(null, igniteTuple));
Assert.AreEqual("Missed key column: KEY", ex!.Message);
}
[Test]
public void TestMissingValTupleFields()
{
var igniteTuple = new IgniteTuple
{
[KeyCol] = 1L
};
var ex = Assert.ThrowsAsync<MarshallerException>(async () => await TableRequiredVal.RecordBinaryView.UpsertAsync(null, igniteTuple));
StringAssert.StartsWith("Column 'VAL' does not allow NULLs", ex!.Message);
}
[Test]
public void TestKvMissingKeyTupleFields()
{
var keyTuple = new IgniteTuple();
var valTuple = new IgniteTuple
{
[ValCol] = "v"
};
var ex = Assert.ThrowsAsync<MarshallerException>(async () => await Table.KeyValueBinaryView.PutAsync(null, keyTuple, valTuple));
Assert.AreEqual("Missed key column: KEY", ex!.Message);
}
[Test]
public void TestKvMissingValTupleFields()
{
var keyTuple = new IgniteTuple
{
[KeyCol] = 1L
};
var valTuple = new IgniteTuple();
var ex = Assert.ThrowsAsync<MarshallerException>(
async () => await TableRequiredVal.KeyValueBinaryView.PutAsync(null, keyTuple, valTuple));
StringAssert.StartsWith("Column 'VAL' does not allow NULLs", ex!.Message);
}
[Test]
public void TestMissingAllFields()
{
var igniteTuple = new IgniteTuple
{
["abc"] = "v"
};
var ex = Assert.ThrowsAsync<ArgumentException>(async () => await TupleView.UpsertAsync(null, igniteTuple));
Assert.AreEqual("Can't map 'IgniteTuple { ABC = v }' to columns 'Int64 KEY, String VAL'. Matching fields not found.", ex!.Message);
}
[Test]
public void TestKvMissingAllFields()
{
var keyTuple = new IgniteTuple
{
["abc"] = "v"
};
var valTuple = new IgniteTuple
{
["x"] = "y"
};
var ex = Assert.ThrowsAsync<ArgumentException>(async () => await Table.KeyValueBinaryView.PutAsync(null, keyTuple, valTuple));
Assert.AreEqual(
"Can't map 'KvPair { Key = IgniteTuple { ABC = v }, Val = IgniteTuple { X = y } }' to columns 'Int64 KEY, String VAL'. " +
"Matching fields not found.",
ex!.Message);
}
[Test]
public void TestDuplicateTupleFields()
{
var tuple = new DuplicateFieldTuple();
var ex = Assert.ThrowsAsync<ArgumentException>(async () => await Table.RecordBinaryView.UpsertAsync(null, tuple));
StringAssert.StartsWith("Duplicate column in Tuple: KEY", ex!.Message);
}
[Test]
public void TestKvDuplicateTupleFields()
{
var tuple = new DuplicateFieldTuple();
var ex = Assert.ThrowsAsync<ArgumentException>(async () => await Table.KeyValueBinaryView.PutAsync(null, tuple, tuple));
StringAssert.StartsWith("Duplicate column in Key portion of KeyValue pair: KEY", ex!.Message);
}
[Test]
public void TestUnmappedPocoFields()
{
var poco = new PocoUnmapped(1, "x", "y");
var ex = Assert.ThrowsAsync<ArgumentException>(
async () => await Table.GetRecordView<PocoUnmapped>().UpsertAsync(null, poco));
Assert.AreEqual(
"Record of type Apache.Ignite.Tests.Table.SchemaValidationTest+PocoUnmapped doesn't match schema: " +
"schemaVersion=1, extraColumns=Foo, Bar",
ex!.Message);
}
[Test]
public void TestKvUnmappedKeyPocoFields()
{
var poco = new PocoUnmapped(1, "x", "y");
var ex = Assert.ThrowsAsync<ArgumentException>(
async () => await Table.GetKeyValueView<PocoUnmapped, string>().PutAsync(null, poco, "x"));
Assert.AreEqual(
"KeyValue pair of type (Apache.Ignite.Tests.Table.SchemaValidationTest+PocoUnmapped, ) doesn't match schema: " +
"schemaVersion=1, extraColumns=Foo, Bar",
ex!.Message);
}
[Test]
public void TestKvUnmappedValPocoFields()
{
var poco = new PocoUnmapped(1, "x", "y");
var ex = Assert.ThrowsAsync<ArgumentException>(
async () => await Table.GetKeyValueView<long, PocoUnmapped>().PutAsync(null, 1, poco));
Assert.AreEqual(
"KeyValue pair of type (, Apache.Ignite.Tests.Table.SchemaValidationTest+PocoUnmapped) doesn't match schema: " +
"schemaVersion=1, extraColumns=Foo, Bar",
ex!.Message);
}
[Test]
public void TestMissingKeyPocoFields()
{
var ex = Assert.ThrowsAsync<MarshallerException>(async () => await Table.GetRecordView<ValPoco>().UpsertAsync(null, new ValPoco()));
Assert.AreEqual("Missed key column: KEY", ex!.Message);
}
[Test]
public void TestMissingValPocoFields()
{
var ex = Assert.ThrowsAsync<MarshallerException>(
async () => await TableRequiredVal.GetRecordView<KeyPoco>().UpsertAsync(null, new KeyPoco()));
StringAssert.StartsWith("Column 'VAL' does not allow NULLs", ex!.Message);
}
[Test]
public void TestKvMissingKeyPocoFields()
{
var ex = Assert.ThrowsAsync<MarshallerException>(
async () => await Table.GetKeyValueView<ValPoco, string>().PutAsync(null, new ValPoco(), "x"));
Assert.AreEqual("Missed key column: KEY", ex!.Message);
}
[Test]
public void TestKvMissingValPocoFields()
{
var ex = Assert.ThrowsAsync<MarshallerException>(
async () => await TableRequiredVal.GetKeyValueView<long, KeyPoco>().PutAsync(null, 1L, new KeyPoco()));
StringAssert.StartsWith("Column 'VAL' does not allow NULLs", ex!.Message);
}
[Test]
public void TestMissingAllPocoFields()
{
var ex = Assert.ThrowsAsync<ArgumentException>(
async () => await Table.GetRecordView<PocoNoMatchingFields>().UpsertAsync(null, new PocoNoMatchingFields("x", 1)));
Assert.AreEqual(
"Can't map 'Apache.Ignite.Tests.Table.SchemaValidationTest+PocoNoMatchingFields' to columns 'Int64 KEY, String VAL'. " +
"Matching fields not found.",
ex!.Message);
}
[Test]
public void TestKvMissingAllPocoFields()
{
var poco = new PocoNoMatchingFields("x", 1);
var ex = Assert.ThrowsAsync<ArgumentException>(
async () => await Table.GetKeyValueView<PocoNoMatchingFields, PocoNoMatchingFields>().PutAsync(null, poco, poco));
Assert.AreEqual(
"Can't map 'Apache.Ignite.Tests.Table.SchemaValidationTest+PocoNoMatchingFields' " +
"and 'Apache.Ignite.Tests.Table.SchemaValidationTest+PocoNoMatchingFields' to columns 'Int64 KEY, String VAL'. " +
"Matching fields not found.",
ex!.Message);
}
[Test]
public void TestKvDuplicatePocoFields()
{
var ex = Assert.ThrowsAsync<ArgumentException>(
async () => await Table.GetRecordView<DuplicateFieldPoco>().UpsertAsync(null, new DuplicateFieldPoco()));
StringAssert.StartsWith("Column 'VAL' maps to more than one field", ex!.Message);
}
// ReSharper disable NotAccessedPositionalProperty.Local
private record PocoUnmapped(long Key, string? Foo, string? Bar);
private record PocoNoMatchingFields(string Name, int Age);
private class DuplicateFieldTuple : IIgniteTuple
{
public int FieldCount => 3;
public object? this[int ordinal]
{
get => ordinal == 0 ? 1L : "1";
set {}
}
public object? this[string name]
{
get => name == "KEY" ? 1L : "1";
set {}
}
public string GetName(int ordinal) => "KEY";
public int GetOrdinal(string name) => name == "KEY" ? 0 : 1;
}
[SuppressMessage("ReSharper", "UnusedMember.Local", Justification = "Reviewed")]
private class DuplicateFieldPoco
{
public long Key { get; set; }
[Column("VAL")]
public string? Val { get; set; }
[Column("VAL")]
public string? Val2 { get; set; }
}
}