mirror of https://github.com/AMT-Cheif/drift.git
Migrate drift file analysis tests to new analyzer
This commit is contained in:
parent
292dd9946d
commit
23b0c8a362
|
@ -33,6 +33,17 @@ class DriftAnalysisError {
|
|||
return DriftAnalysisError(sql.span, message);
|
||||
}
|
||||
|
||||
factory DriftAnalysisError.fromSqlError(sql.AnalysisError error) {
|
||||
var message = error.message ?? '';
|
||||
if (error.type == sql.AnalysisErrorType.notSupportedInDesiredVersion) {
|
||||
message =
|
||||
'$message\nNote: You can change the assumed sqlite version with build '
|
||||
'options. See https://drift.simonbinder.eu/options/#assumed-sql-environment for details!';
|
||||
}
|
||||
|
||||
return DriftAnalysisError(error.span, message);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
final span = this.span;
|
||||
|
|
|
@ -44,7 +44,7 @@ abstract class DriftElementResolver<T extends DiscoveredElement>
|
|||
} else if (foundElement is TypeAliasElement) {
|
||||
final innerType = foundElement.aliasedType;
|
||||
if (innerType is InterfaceType) {
|
||||
return FoundDartClass(innerType.element2, innerType.typeArguments);
|
||||
return FoundDartClass(innerType.element, innerType.typeArguments);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -83,7 +83,6 @@ abstract class DriftElementResolver<T extends DiscoveredElement>
|
|||
}
|
||||
|
||||
void reportLint(AnalysisError parserError) {
|
||||
reportError(
|
||||
DriftAnalysisError(parserError.span, parserError.message ?? ''));
|
||||
reportError(DriftAnalysisError.fromSqlError(parserError));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ extension FindDartClass on LocalElementResolver {
|
|||
} else if (foundElement is TypeAliasElement) {
|
||||
final innerType = foundElement.aliasedType;
|
||||
if (innerType is InterfaceType) {
|
||||
return FoundDartClass(innerType.element2, innerType.typeArguments);
|
||||
return FoundDartClass(innerType.element, innerType.typeArguments);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,8 +2,11 @@ import 'package:analyzer/dart/ast/ast.dart' as dart;
|
|||
import 'package:collection/collection.dart';
|
||||
import 'package:drift/drift.dart' show DriftSqlType;
|
||||
import 'package:recase/recase.dart';
|
||||
import 'package:sqlparser/sqlparser.dart';
|
||||
import 'package:sqlparser/sqlparser.dart' hide PrimaryKeyColumn, UniqueColumn;
|
||||
import 'package:sqlparser/sqlparser.dart' as sql;
|
||||
import 'package:sqlparser/utils/node_to_text.dart';
|
||||
|
||||
import '../../../utils/string_escaper.dart';
|
||||
import '../../backend.dart';
|
||||
import '../../driver/error.dart';
|
||||
import '../../results/results.dart';
|
||||
|
@ -52,6 +55,7 @@ class DriftTableResolver extends LocalElementResolver<DiscoveredDriftTable> {
|
|||
final nullable = column.type.nullable != false;
|
||||
final constraints = <DriftColumnConstraint>[];
|
||||
AppliedTypeConverter? converter;
|
||||
AnnotatedDartCode? defaultArgument;
|
||||
|
||||
final typeName = column.definition?.typeName;
|
||||
final enumMatch =
|
||||
|
@ -69,8 +73,8 @@ class DriftTableResolver extends LocalElementResolver<DiscoveredDriftTable> {
|
|||
));
|
||||
} else {
|
||||
converter = readEnumConverter(
|
||||
(msg) =>
|
||||
DriftAnalysisError.inDriftFile(column.definition ?? stmt, msg),
|
||||
(msg) => reportError(
|
||||
DriftAnalysisError.inDriftFile(column.definition ?? stmt, msg)),
|
||||
dartClass.classElement.thisType,
|
||||
);
|
||||
}
|
||||
|
@ -87,7 +91,7 @@ class DriftTableResolver extends LocalElementResolver<DiscoveredDriftTable> {
|
|||
if (converter != null) {
|
||||
reportError(DriftAnalysisError.inDriftFile(
|
||||
constraint,
|
||||
'Multiple type converters applied to this converter, ignoring '
|
||||
'Multiple type converters applied to this column, ignoring '
|
||||
'this one.'));
|
||||
continue;
|
||||
}
|
||||
|
@ -125,6 +129,26 @@ class DriftTableResolver extends LocalElementResolver<DiscoveredDriftTable> {
|
|||
}
|
||||
}
|
||||
}
|
||||
} else if (constraint is GeneratedAs) {
|
||||
constraints.add(ColumnGeneratedAs(
|
||||
AnnotatedDartCode.build((b) => b
|
||||
..addText('const ')
|
||||
..addSymbol('CustomExpression', AnnotatedDartCode.drift)
|
||||
..addText('(')
|
||||
..addText(asDartLiteral(constraint.expression.toSql()))
|
||||
..addText(')')),
|
||||
constraint.stored));
|
||||
} else if (constraint is Default) {
|
||||
defaultArgument = AnnotatedDartCode.build((b) => b
|
||||
..addText('const ')
|
||||
..addSymbol('CustomExpression', AnnotatedDartCode.drift)
|
||||
..addText('(')
|
||||
..addText(asDartLiteral(constraint.expression.toSql()))
|
||||
..addText(')'));
|
||||
} else if (constraint is sql.PrimaryKeyColumn) {
|
||||
constraints.add(PrimaryKeyColumn(constraint.autoIncrement));
|
||||
} else if (constraint is sql.UniqueColumn) {
|
||||
constraints.add(UniqueColumn());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -135,6 +159,7 @@ class DriftTableResolver extends LocalElementResolver<DiscoveredDriftTable> {
|
|||
nameInDart: overriddenDartName ?? ReCase(column.name).camelCase,
|
||||
constraints: constraints,
|
||||
typeConverter: converter,
|
||||
defaultArgument: defaultArgument,
|
||||
declaration: DriftDeclaration.driftFile(
|
||||
column.definition?.nameToken ?? stmt,
|
||||
state.ownId.libraryUri,
|
||||
|
|
|
@ -53,8 +53,7 @@ class FileAnalyzer {
|
|||
queries[query.name] = analyzer.analyze(query);
|
||||
|
||||
for (final error in analyzer.lints) {
|
||||
result.analysisErrors.add(DriftAnalysisError(
|
||||
error.span, 'Error in ${query.name}: ${error.message}'));
|
||||
result.analysisErrors.add(DriftAnalysisError.fromSqlError(error));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -91,8 +90,7 @@ class FileAnalyzer {
|
|||
..declaredInDriftFile = true;
|
||||
|
||||
for (final error in analyzer.lints) {
|
||||
result.analysisErrors
|
||||
.add(DriftAnalysisError(error.span, error.message ?? ''));
|
||||
result.analysisErrors.add(DriftAnalysisError.fromSqlError(error));
|
||||
}
|
||||
} else if (element is DriftView) {
|
||||
final source = element.source;
|
||||
|
|
|
@ -229,7 +229,7 @@ AppliedTypeConverter readEnumConverter(
|
|||
reportError('Not a class: `$enumType`');
|
||||
}
|
||||
|
||||
final creatingClass = enumType.element2;
|
||||
final creatingClass = enumType.element;
|
||||
if (creatingClass is! EnumElement) {
|
||||
reportError('Not an enum: `${creatingClass!.displayName}`');
|
||||
}
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
@Tags(['analyzer'])
|
||||
import 'package:drift/drift.dart' as drift;
|
||||
import 'package:drift_dev/src/analysis/results/results.dart';
|
||||
import 'package:sqlparser/sqlparser.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import '../../test_utils.dart';
|
||||
|
||||
void main() {
|
||||
test('view created', () async {
|
||||
final state = TestBackend.inTest({
|
||||
'foo|lib/table.drift': '''
|
||||
CREATE TABLE t (id INTEGER NOT NULL PRIMARY KEY, name TEXT NOT NULL);
|
||||
''',
|
||||
'foo|lib/a.drift': '''
|
||||
import 'table.drift';
|
||||
CREATE VIEW random_view AS
|
||||
SELECT name FROM t WHERE id % 2 = 0;
|
||||
''',
|
||||
});
|
||||
|
||||
final file = await state.analyze('package:foo/a.drift');
|
||||
final view = file.analyzedElements.single as DriftView;
|
||||
|
||||
expect(view.columns, [
|
||||
isA<DriftColumn>()
|
||||
.having((e) => e.sqlType, 'sqlType', drift.DriftSqlType.string)
|
||||
]);
|
||||
|
||||
expect(view.references,
|
||||
[isA<DriftTable>().having((t) => t.schemaName, 'schemaName', 't')]);
|
||||
|
||||
state.expectNoErrors();
|
||||
});
|
||||
|
||||
test('view created from another view', () async {
|
||||
final state = TestBackend.inTest({
|
||||
'foo|lib/table.drift': '''
|
||||
CREATE TABLE t (id INTEGER NOT NULL PRIMARY KEY, name TEXT NOT NULL);
|
||||
''',
|
||||
'foo|lib/a.drift': '''
|
||||
import 'table.drift';
|
||||
|
||||
CREATE VIEW parent_view AS
|
||||
SELECT id, name FROM t WHERE id % 2 = 0;
|
||||
|
||||
CREATE VIEW child_view AS
|
||||
SELECT name FROM parent_view;
|
||||
''',
|
||||
});
|
||||
|
||||
final file = await state.analyze('package:foo/a.drift');
|
||||
final parentView =
|
||||
file.analysis[file.id('parent_view')]!.result as DriftView;
|
||||
final childView = file.analysis[file.id('child_view')]!.result as DriftView;
|
||||
|
||||
expect(parentView.columns, hasLength(2));
|
||||
expect(childView.columns, [
|
||||
isA<DriftColumn>()
|
||||
.having((e) => e.sqlType, 'sqlType', drift.DriftSqlType.string)
|
||||
]);
|
||||
|
||||
expect(parentView.references.map((e) => e.id.name), ['t']);
|
||||
expect(childView.references, [parentView]);
|
||||
expect(childView.transitiveTableReferences.map((e) => e.schemaName), ['t']);
|
||||
|
||||
state.expectNoErrors();
|
||||
});
|
||||
|
||||
test('view without table', () async {
|
||||
final state = TestBackend.inTest({
|
||||
'foo|lib/a.drift': '''
|
||||
CREATE VIEW random_view AS
|
||||
SELECT name FROM t WHERE id % 2 = 0;
|
||||
''',
|
||||
});
|
||||
|
||||
final file = await state.analyze('package:foo/a.drift');
|
||||
|
||||
expect(
|
||||
file.allErrors, contains(isDriftError(contains('Could not find t'))));
|
||||
});
|
||||
|
||||
test('does not allow nested columns', () async {
|
||||
final state = TestBackend.inTest({
|
||||
'foo|lib/a.drift': '''
|
||||
CREATE TABLE foo (bar INTEGER NOT NULL PRIMARY KEY);
|
||||
|
||||
CREATE VIEW v AS SELECT foo.** FROM foo;
|
||||
''',
|
||||
});
|
||||
|
||||
final file = await state.analyze('package:foo/a.drift');
|
||||
|
||||
expect(file.allErrors, [
|
||||
isDriftError(
|
||||
contains('Nested star columns may only appear in a top-level select '
|
||||
'query.'))
|
||||
]);
|
||||
});
|
||||
}
|
|
@ -1,4 +1,3 @@
|
|||
import 'package:analyzer/dart/element/type.dart';
|
||||
import 'package:drift_dev/src/analysis/results/results.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
@Tags(['analyzer'])
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import '../../test_utils.dart';
|
||||
|
||||
void main() {
|
||||
test('reports an error when importing a part file into .drift', () async {
|
||||
final state = TestBackend.inTest({
|
||||
'a|lib/base.dart': '''
|
||||
import 'package:drift/drift.dart';
|
||||
|
||||
part 'tables.dart';
|
||||
''',
|
||||
'a|lib/tables.dart': '''
|
||||
part of 'base.dart';
|
||||
|
||||
class Events extends Table {
|
||||
IntColumn get id => integer().autoIncrement()();
|
||||
|
||||
RealColumn get sumVal => real().withDefault(Constant(0))();
|
||||
}
|
||||
|
||||
class Records extends Table {
|
||||
IntColumn get eventId => integer()();
|
||||
|
||||
RealColumn get value => real().nullable()();
|
||||
}
|
||||
''',
|
||||
'a|lib/file.drift': '''
|
||||
import 'tables.dart';
|
||||
''',
|
||||
});
|
||||
|
||||
final file = await state.analyze('package:a/file.drift');
|
||||
expect(file.allErrors, [
|
||||
isDriftError(contains("does not exist or can't be imported."))
|
||||
.withSpan("import 'tables.dart';")
|
||||
]);
|
||||
});
|
||||
}
|
|
@ -1,12 +1,11 @@
|
|||
import 'package:drift_dev/moor_generator.dart';
|
||||
import 'package:drift/drift.dart' show DriftSqlType;
|
||||
import 'package:drift_dev/src/analysis/results/results.dart';
|
||||
import 'package:drift_dev/src/analyzer/drift/moor_ffi_extension.dart';
|
||||
import 'package:drift_dev/src/analyzer/errors.dart';
|
||||
import 'package:drift_dev/src/analyzer/options.dart';
|
||||
import 'package:drift_dev/src/analyzer/runner/results.dart';
|
||||
import 'package:sqlparser/sqlparser.dart' hide ResultColumn;
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import '../utils.dart';
|
||||
import '../../test_utils.dart';
|
||||
|
||||
void main() {
|
||||
late SqlEngine engine;
|
||||
|
@ -83,41 +82,38 @@ void main() {
|
|||
);
|
||||
});
|
||||
|
||||
test('integration tests with moor files and experimental inference',
|
||||
test('integration tests with drift files and experimental inference',
|
||||
() async {
|
||||
final state = TestState.withContent(
|
||||
final state = TestBackend.inTest(
|
||||
const {
|
||||
'foo|lib/a.moor': '''
|
||||
'foo|lib/a.drift': '''
|
||||
CREATE TABLE numbers (foo REAL NOT NULL);
|
||||
|
||||
query: SELECT pow(oid, foo) FROM numbers;
|
||||
''',
|
||||
'foo|lib/b.moor': '''
|
||||
import 'a.moor';
|
||||
'foo|lib/b.drift': '''
|
||||
import 'a.drift';
|
||||
|
||||
wrongArgs: SELECT sin(oid, foo) FROM numbers;
|
||||
'''
|
||||
},
|
||||
options: const DriftOptions.defaults(modules: [SqlModule.moor_ffi]),
|
||||
);
|
||||
addTearDown(state.close);
|
||||
|
||||
final fileA = await state.analyze('package:foo/a.moor');
|
||||
final fileA = await state.analyze('package:foo/a.drift');
|
||||
expect(fileA.allErrors, isEmpty);
|
||||
|
||||
expect(fileA.errors.errors, isEmpty);
|
||||
final resultA = fileA.currentResult as ParsedDriftFile;
|
||||
|
||||
final queryInA = resultA.resolvedQueries!.single as SqlSelectQuery;
|
||||
final queryInA =
|
||||
fileA.fileAnalysis!.resolvedQueries.values.single as SqlSelectQuery;
|
||||
expect(
|
||||
queryInA.resultSet.columns.single,
|
||||
const TypeMatcher<ResultColumn>()
|
||||
.having((e) => e.type, 'type', DriftSqlType.double),
|
||||
.having((e) => e.sqlType, 'type', DriftSqlType.double),
|
||||
);
|
||||
|
||||
final fileB = await state.analyze('package:foo/b.moor');
|
||||
expect(fileB.errors.errors, [
|
||||
const TypeMatcher<ErrorInDriftFile>()
|
||||
.having((e) => e.span?.text, 'span.text', 'sin(oid, foo)')
|
||||
final fileB = await state.analyze('package:foo/b.drift');
|
||||
expect(fileB.allErrors, [
|
||||
isDriftError('sin expects 1 arguments, got 2.').withSpan('sin(oid, foo)')
|
||||
]);
|
||||
});
|
||||
}
|
|
@ -1,11 +1,11 @@
|
|||
import 'package:drift_dev/src/analyzer/runner/results.dart';
|
||||
import 'package:drift_dev/src/analysis/results/results.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import '../utils.dart';
|
||||
import '../../test_utils.dart';
|
||||
|
||||
void main() {
|
||||
test('moor files can import original dart source', () async {
|
||||
final state = TestState.withContent({
|
||||
test('drift files can import original dart source', () async {
|
||||
final state = TestBackend.inTest({
|
||||
'a|lib/base.dart': r'''
|
||||
import 'package:drift/drift.dart';
|
||||
|
||||
|
@ -29,25 +29,23 @@ class Units extends Table {
|
|||
IntColumn get id => integer().autoIncrement()();
|
||||
}
|
||||
|
||||
@DriftDatabase(include: {'customizedSQL.moor'})
|
||||
@DriftDatabase(include: {'customizedSQL.drift'})
|
||||
class AppDatabase extends _$AppDatabase {
|
||||
AppDatabase()
|
||||
: super(FlutterQueryExecutor.inDatabaseFolder(
|
||||
path: "db.sqlite", logStatements: true));
|
||||
}
|
||||
''',
|
||||
'a|lib/customizedSQL.moor': '''
|
||||
'a|lib/customizedSQL.drift': '''
|
||||
import 'base.dart';
|
||||
|
||||
create trigger addVal after insert on records when id = NEW.event_id BEGIN update events set sum_val = sum_val + NEW.value; END;
|
||||
''',
|
||||
});
|
||||
addTearDown(state.close);
|
||||
|
||||
final file = await state.analyze('package:a/base.dart');
|
||||
final result = file.currentResult as ParsedDartFile;
|
||||
final db = result.declaredDatabases.single;
|
||||
final db = file.fileAnalysis!.resolvedDatabases.values.single;
|
||||
|
||||
expect(db.tables, hasLength(3));
|
||||
expect(db.availableElements.whereType<DriftTable>(), hasLength(3));
|
||||
});
|
||||
}
|
|
@ -1,13 +1,14 @@
|
|||
import 'package:drift_dev/src/analysis/results/results.dart';
|
||||
import 'package:drift_dev/src/analyzer/options.dart';
|
||||
import 'package:sqlparser/sqlparser.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import '../utils.dart';
|
||||
import '../../test_utils.dart';
|
||||
|
||||
void main() {
|
||||
// https://github.com/simolus3/drift/issues/2097#issuecomment-1273008383
|
||||
test('supports fts5 tables with external content', () async {
|
||||
final state = TestState.withContent(
|
||||
test('virtual columns are not required for inserts', () async {
|
||||
final state = TestBackend.inTest(
|
||||
{
|
||||
'foo|lib/a.drift': r'''
|
||||
CREATE TABLE IF NOT EXISTS nodes (
|
||||
|
@ -25,12 +26,13 @@ insertNode: INSERT INTO nodes VALUES(json(?));
|
|||
|
||||
final result = await state.analyze('package:foo/a.drift');
|
||||
|
||||
expect(result.errors.errors, isEmpty);
|
||||
final table = result.analysis[result.id('nodes')]!.result as DriftTable;
|
||||
|
||||
final table = result.currentResult!.declaredTables.single;
|
||||
expect(table.sqlName, 'nodes');
|
||||
expect(table.schemaName, 'nodes');
|
||||
expect(table.columns, hasLength(2));
|
||||
expect(table.isColumnRequiredForInsert(table.columns[0]), isFalse);
|
||||
expect(table.isColumnRequiredForInsert(table.columns[1]), isFalse);
|
||||
|
||||
state.expectNoErrors();
|
||||
});
|
||||
}
|
|
@ -1,13 +1,13 @@
|
|||
import 'package:drift_dev/src/analyzer/options.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import '../utils.dart';
|
||||
import '../../test_utils.dart';
|
||||
|
||||
void main() {
|
||||
// Regression test for https://github.com/simolus3/drift/issues/754
|
||||
test('supports fts5 tables with external content', () async {
|
||||
final state = TestState.withContent({
|
||||
'foo|lib/a.moor': '''
|
||||
final state = TestBackend.inTest({
|
||||
'foo|lib/a.drift': '''
|
||||
CREATE TABLE tbl(a INTEGER PRIMARY KEY, b TEXT, c TEXT);
|
||||
CREATE VIRTUAL TABLE fts_idx USING fts5(b, c, content='tbl', content_rowid='a');
|
||||
|
||||
|
@ -27,10 +27,11 @@ END;
|
|||
''',
|
||||
}, options: const DriftOptions.defaults(modules: [SqlModule.fts5]));
|
||||
|
||||
final result = await state.analyze('package:foo/a.moor');
|
||||
final result = await state.analyze('package:foo/a.drift');
|
||||
|
||||
// The generator used to crash while analyzing, so consider the test passed
|
||||
// if it can analyze the file and sees that there aren't any errors.
|
||||
expect(result.errors.errors, isEmpty);
|
||||
state.expectNoErrors();
|
||||
expect(result.analyzedElements, hasLength(5));
|
||||
});
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
import 'package:drift_dev/src/analyzer/options.dart';
|
||||
import 'package:sqlparser/sqlparser.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import '../../test_utils.dart';
|
||||
|
||||
const _content = {
|
||||
'a|lib/main.drift': '''
|
||||
CREATE TABLE foo (
|
||||
id INTEGER NOT NULL PRIMARY KEY,
|
||||
content TEXT NOT NULL UNIQUE,
|
||||
content2 TEXT NOT NULL UNIQUE
|
||||
);
|
||||
|
||||
query: INSERT INTO foo VALUES (?, ?, ?)
|
||||
ON CONFLICT (content) DO NOTHING
|
||||
ON CONFLICT (content2) DO UPDATE SET content2 = 'duplicate';
|
||||
''',
|
||||
};
|
||||
|
||||
void main() {
|
||||
test('does not support newer sqlite features by default', () async {
|
||||
final state = TestBackend.inTest(_content);
|
||||
|
||||
final file = await state.analyze('package:a/main.drift');
|
||||
expect(
|
||||
file.allErrors,
|
||||
[
|
||||
isDriftError(
|
||||
allOf(
|
||||
contains('require sqlite version 3.35 or later'),
|
||||
contains(
|
||||
'You can change the assumed sqlite version with build options.'),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
});
|
||||
|
||||
test('supports newer sqlite features', () async {
|
||||
final state = TestBackend.inTest(
|
||||
_content,
|
||||
options: const DriftOptions.defaults(
|
||||
sqliteAnalysisOptions: SqliteAnalysisOptions(
|
||||
version: SqliteVersion.v3_35,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await state.analyze('package:a/main.drift');
|
||||
state.expectNoErrors();
|
||||
});
|
||||
}
|
|
@ -35,7 +35,7 @@ CREATE TABLE b (
|
|||
|
||||
expect(aFoo.sqlType, DriftSqlType.int);
|
||||
expect(aFoo.nullable, isFalse);
|
||||
expect(aFoo.constraints, isEmpty);
|
||||
expect(aFoo.constraints, [isA<PrimaryKeyColumn>()]);
|
||||
expect(aFoo.customConstraints, isNull);
|
||||
|
||||
expect(aBar.sqlType, DriftSqlType.int);
|
||||
|
@ -53,4 +53,128 @@ CREATE TABLE b (
|
|||
expect(bBar.constraints, isEmpty);
|
||||
expect(bBar.customConstraints, isNull);
|
||||
});
|
||||
|
||||
test('recognizes aliases to rowid', () async {
|
||||
final state = TestBackend.inTest({
|
||||
'foo|lib/a.drift': '''
|
||||
CREATE TABLE users (
|
||||
id INTEGER PRIMARY KEY,
|
||||
name TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE users2 (
|
||||
id INTEGER,
|
||||
name TEXT NOT NULL,
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
'''
|
||||
});
|
||||
|
||||
final result = await state.analyze('package:foo/a.drift');
|
||||
|
||||
final users1 = result.analysis[result.id('users')]!.result as DriftTable;
|
||||
final users2 = result.analysis[result.id('users2')]!.result as DriftTable;
|
||||
|
||||
expect(users1.isColumnRequiredForInsert(users1.columns[0]), isFalse);
|
||||
expect(users1.isColumnRequiredForInsert(users1.columns[1]), isTrue);
|
||||
|
||||
expect(users2.isColumnRequiredForInsert(users2.columns[0]), isFalse);
|
||||
expect(users2.isColumnRequiredForInsert(users2.columns[1]), isTrue);
|
||||
});
|
||||
|
||||
test('parses enum columns', () async {
|
||||
final state = TestBackend.inTest({
|
||||
'a|lib/a.drift': '''
|
||||
import 'enum.dart';
|
||||
|
||||
CREATE TABLE foo (
|
||||
fruit ENUM(Fruits) NOT NULL,
|
||||
another ENUM(DoesNotExist) NOT NULL
|
||||
);
|
||||
''',
|
||||
'a|lib/enum.dart': '''
|
||||
enum Fruits {
|
||||
apple, orange, banana
|
||||
}
|
||||
''',
|
||||
});
|
||||
|
||||
final file = await state.analyze('package:a/a.drift');
|
||||
final table = file.analyzedElements.single as DriftTable;
|
||||
final column = table.columns.singleWhere((c) => c.nameInSql == 'fruit');
|
||||
|
||||
expect(column.sqlType, DriftSqlType.int);
|
||||
expect(
|
||||
column.typeConverter,
|
||||
isA<AppliedTypeConverter>()
|
||||
.having(
|
||||
(e) => e.expression.toString(),
|
||||
'expression',
|
||||
contains('EnumIndexConverter<Fruits>'),
|
||||
)
|
||||
.having((e) => e.dartType.getDisplayString(withNullability: true),
|
||||
'dartType', 'Fruits'),
|
||||
);
|
||||
|
||||
expect(
|
||||
file.allErrors,
|
||||
contains(isDriftError(contains('Type DoesNotExist could not be found'))
|
||||
.withSpan('ENUM(DoesNotExist)')),
|
||||
);
|
||||
});
|
||||
|
||||
test('does not allow converters for enum columns', () async {
|
||||
final state = TestBackend.inTest({
|
||||
'a|lib/a.drift': '''
|
||||
import 'enum.dart';
|
||||
|
||||
CREATE TABLE foo (
|
||||
fruit ENUM(Fruits) NOT NULL MAPPED BY `MyConverter()`
|
||||
);
|
||||
''',
|
||||
'a|lib/enum.dart': '''
|
||||
import 'package:drift/drift.dart';
|
||||
|
||||
enum Fruits {
|
||||
apple, orange, banana
|
||||
}
|
||||
|
||||
class MyConverter extends TypeConverter<String, String> {}
|
||||
''',
|
||||
});
|
||||
|
||||
final file = await state.analyze('package:a/a.drift');
|
||||
|
||||
expect(
|
||||
file.allErrors,
|
||||
[
|
||||
isDriftError(
|
||||
'Multiple type converters applied to this column, ignoring this one.')
|
||||
.withSpan('MAPPED BY `MyConverter()`')
|
||||
],
|
||||
);
|
||||
});
|
||||
|
||||
test('does not allow enum types for non-enums', () async {
|
||||
final state = TestBackend.inTest({
|
||||
'a|lib/a.drift': '''
|
||||
import 'enum.dart';
|
||||
|
||||
CREATE TABLE foo (
|
||||
fruit ENUM(NotAnEnum) NOT NULL
|
||||
);
|
||||
''',
|
||||
'a|lib/enum.dart': '''
|
||||
class NotAnEnum {}
|
||||
''',
|
||||
});
|
||||
|
||||
final file = await state.analyze('package:a/a.drift');
|
||||
expect(file.analyzedElements, hasLength(1));
|
||||
|
||||
expect(
|
||||
file.allErrors,
|
||||
contains(isDriftError('Not an enum: `NotAnEnum`')),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import 'package:drift/drift.dart';
|
||||
import 'package:drift_dev/src/analysis/results/results.dart';
|
||||
import 'package:drift_dev/src/analysis/results/table.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
|
@ -91,23 +93,82 @@ CREATE TABLE b (
|
|||
expect(a.references, [b]);
|
||||
});
|
||||
|
||||
test('non-existing', () async {
|
||||
test('for triggers', () async {
|
||||
final backend = TestBackend.inTest({
|
||||
'a|lib/a.drift': '''
|
||||
import 'b.drift';
|
||||
|
||||
CREATE TRIGGER my_trigger AFTER DELETE ON b BEGIN
|
||||
INSERT INTO deleted_b VALUES (old.bar);
|
||||
END;
|
||||
''',
|
||||
'a|lib/b.drift': '''
|
||||
CREATE TABLE b (
|
||||
bar INTEGER NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE deleted_b (
|
||||
bar INTEGER NOT NULL
|
||||
);
|
||||
''',
|
||||
});
|
||||
|
||||
final file = await backend.analyze('package:a/a.drift');
|
||||
backend.expectNoErrors();
|
||||
|
||||
final trigger = file.analyzedElements.single as DriftTrigger;
|
||||
expect(trigger.references, [
|
||||
isA<DriftTable>().having((e) => e.schemaName, 'schemaName', 'b'),
|
||||
isA<DriftTable>()
|
||||
.having((e) => e.schemaName, 'schemaName', 'deleted_b'),
|
||||
]);
|
||||
|
||||
expect(trigger.writes, [
|
||||
isA<WrittenDriftTable>()
|
||||
.having((e) => e.table.schemaName, 'table.schemaName', 'deleted_b')
|
||||
.having((e) => e.kind, 'kind', UpdateKind.insert),
|
||||
]);
|
||||
});
|
||||
|
||||
test('for indices', () async {});
|
||||
|
||||
group('non-existing', () {
|
||||
test('from table', () async {
|
||||
final backend = TestBackend.inTest({
|
||||
'a|lib/a.drift': '''
|
||||
CREATE TABLE a (
|
||||
foo INTEGER PRIMARY KEY,
|
||||
bar INTEGER REFERENCES b (bar)
|
||||
);
|
||||
''',
|
||||
});
|
||||
|
||||
final state = await backend.driver
|
||||
.resolveElements(Uri.parse('package:a/a.drift'));
|
||||
expect(state.errorsDuringDiscovery, isEmpty);
|
||||
|
||||
final resultA = state.analysis.values.single;
|
||||
expect(resultA.errorsDuringAnalysis,
|
||||
[isDriftError('`b` could not be found in any import.')]);
|
||||
});
|
||||
test('in a trigger', () async {
|
||||
final backend = TestBackend.inTest(const {
|
||||
'foo|lib/a.drift': '''
|
||||
CREATE TRIGGER IF NOT EXISTS foo BEFORE DELETE ON bar BEGIN
|
||||
END;
|
||||
''',
|
||||
});
|
||||
|
||||
final state =
|
||||
await backend.driver.resolveElements(Uri.parse('package:a/a.drift'));
|
||||
expect(state.errorsDuringDiscovery, isEmpty);
|
||||
final file = await backend.analyze('package:foo/a.drift');
|
||||
|
||||
final resultA = state.analysis.values.single;
|
||||
expect(resultA.errorsDuringAnalysis,
|
||||
[isDriftError('This reference could not be found in any import.')]);
|
||||
expect(
|
||||
file.allErrors,
|
||||
contains(
|
||||
isDriftError(contains('`bar` could not be found in any import'))
|
||||
.withSpan('bar'),
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,125 +0,0 @@
|
|||
@Tags(['analyzer'])
|
||||
import 'package:drift_dev/moor_generator.dart';
|
||||
import 'package:drift_dev/src/analyzer/errors.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import '../utils.dart';
|
||||
|
||||
void main() {
|
||||
test('parses enum columns', () async {
|
||||
final state = TestState.withContent({
|
||||
'foo|lib/a.moor': '''
|
||||
import 'enum.dart';
|
||||
|
||||
CREATE TABLE foo (
|
||||
fruit ENUM(Fruits) NOT NULL,
|
||||
another ENUM(DoesNotExist) NOT NULL
|
||||
);
|
||||
''',
|
||||
'foo|lib/enum.dart': '''
|
||||
enum Fruits {
|
||||
apple, orange, banane
|
||||
}
|
||||
''',
|
||||
});
|
||||
|
||||
final file = await state.analyze('package:foo/a.moor');
|
||||
final table = file.currentResult!.declaredTables.single;
|
||||
final column = table.columns.singleWhere((c) => c.name.name == 'fruit');
|
||||
|
||||
state.close();
|
||||
|
||||
expect(column.type, DriftSqlType.int);
|
||||
expect(
|
||||
column.typeConverter,
|
||||
isA<UsedTypeConverter>()
|
||||
.having(
|
||||
(e) => e.expression,
|
||||
'expression',
|
||||
contains('EnumIndexConverter<Fruits>'),
|
||||
)
|
||||
.having(
|
||||
(e) => e.dartType.getDisplayString(withNullability: false),
|
||||
'mappedType',
|
||||
'Fruits',
|
||||
),
|
||||
);
|
||||
|
||||
expect(
|
||||
file.errors.errors,
|
||||
contains(
|
||||
isA<DriftError>().having(
|
||||
(e) => e.message,
|
||||
'message',
|
||||
contains('Type DoesNotExist could not be found'),
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
test('does not allow converters for enum columns', () async {
|
||||
final state = TestState.withContent({
|
||||
'foo|lib/a.moor': '''
|
||||
import 'enum.dart';
|
||||
|
||||
CREATE TABLE foo (
|
||||
fruit ENUM(Fruits) NOT NULL MAPPED BY `MyConverter()`
|
||||
);
|
||||
''',
|
||||
'foo|lib/enum.dart': '''
|
||||
import 'package:drift/drift.dart';
|
||||
|
||||
enum Fruits {
|
||||
apple, orange, banane
|
||||
}
|
||||
|
||||
class MyConverter extends TypeConverter<String, String> {}
|
||||
''',
|
||||
});
|
||||
|
||||
final file = await state.analyze('package:foo/a.moor');
|
||||
state.close();
|
||||
|
||||
expect(
|
||||
file.errors.errors,
|
||||
contains(
|
||||
isA<DriftError>().having(
|
||||
(e) => e.message,
|
||||
'message',
|
||||
contains("can't apply another converter"),
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
test('does not allow enum types for non-enums', () async {
|
||||
final state = TestState.withContent({
|
||||
'foo|lib/a.moor': '''
|
||||
import 'enum.dart';
|
||||
|
||||
CREATE TABLE foo (
|
||||
fruit ENUM(NotAnEnum) NOT NULL
|
||||
);
|
||||
''',
|
||||
'foo|lib/enum.dart': '''
|
||||
class NotAnEnum {}
|
||||
''',
|
||||
});
|
||||
|
||||
final file = await state.analyze('package:foo/a.moor');
|
||||
state.close();
|
||||
|
||||
expect(
|
||||
file.errors.errors,
|
||||
contains(
|
||||
isA<ErrorInDriftFile>()
|
||||
.having(
|
||||
(e) => e.message,
|
||||
'message',
|
||||
allOf(contains('NotAnEnum'), contains('Not an enum')),
|
||||
)
|
||||
.having((e) => e.span?.text, 'span', 'ENUM(NotAnEnum)'),
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
|
@ -1,114 +0,0 @@
|
|||
@Tags(['analyzer'])
|
||||
import 'package:drift_dev/src/analyzer/errors.dart';
|
||||
import 'package:drift_dev/src/model/table.dart';
|
||||
import 'package:sqlparser/sqlparser.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import '../utils.dart';
|
||||
|
||||
void main() {
|
||||
test('view created', () async {
|
||||
final state = TestState.withContent({
|
||||
'foo|lib/table.moor': '''
|
||||
CREATE TABLE t (id INTEGER NOT NULL PRIMARY KEY, name TEXT NOT NULL);
|
||||
''',
|
||||
'foo|lib/a.moor': '''
|
||||
import 'table.moor';
|
||||
CREATE VIEW random_view AS
|
||||
SELECT name FROM t WHERE id % 2 = 0;
|
||||
''',
|
||||
});
|
||||
|
||||
final file = await state.analyze('package:foo/a.moor');
|
||||
final view = file.currentResult!.declaredViews.single;
|
||||
expect(view.parserView!.resolvedColumns.length, equals(1));
|
||||
final column = view.parserView!.resolvedColumns.single;
|
||||
|
||||
state.close();
|
||||
|
||||
expect(column.type!.type, BasicType.text);
|
||||
expect(view.references,
|
||||
contains(isA<DriftTable>().having((t) => t.sqlName, 'sqlName', 't')));
|
||||
expect(file.errors.errors, isEmpty);
|
||||
});
|
||||
|
||||
test('view created from another view', () async {
|
||||
final state = TestState.withContent({
|
||||
'foo|lib/table.moor': '''
|
||||
CREATE TABLE t (id INTEGER NOT NULL PRIMARY KEY, name TEXT NOT NULL);
|
||||
''',
|
||||
'foo|lib/a.moor': '''
|
||||
import 'table.moor';
|
||||
|
||||
CREATE VIEW parent_view AS
|
||||
SELECT id, name FROM t WHERE id % 2 = 0;
|
||||
|
||||
CREATE VIEW child_view AS
|
||||
SELECT name FROM parent_view;
|
||||
''',
|
||||
});
|
||||
|
||||
final file = await state.analyze('package:foo/a.moor');
|
||||
final parentView = file.currentResult!.declaredViews
|
||||
.singleWhere((element) => element.name == 'parent_view');
|
||||
final childView = file.currentResult!.declaredViews
|
||||
.singleWhere((element) => element.name == 'child_view');
|
||||
expect(parentView.parserView!.resolvedColumns.length, equals(2));
|
||||
expect(childView.parserView!.resolvedColumns.length, equals(1));
|
||||
final column = childView.parserView!.resolvedColumns.single;
|
||||
|
||||
state.close();
|
||||
|
||||
expect(parentView.references.map((e) => e.displayName), ['t']);
|
||||
expect(childView.references, [parentView]);
|
||||
expect(
|
||||
childView.transitiveTableReferences.map((e) => e.displayName), ['t']);
|
||||
|
||||
expect(file.errors.errors, isEmpty);
|
||||
expect(column.type!.type, BasicType.text);
|
||||
});
|
||||
|
||||
test('view without table', () async {
|
||||
final state = TestState.withContent({
|
||||
'foo|lib/a.moor': '''
|
||||
CREATE VIEW random_view AS
|
||||
SELECT name FROM t WHERE id % 2 = 0;
|
||||
''',
|
||||
});
|
||||
|
||||
final file = await state.analyze('package:foo/a.moor');
|
||||
|
||||
state.close();
|
||||
|
||||
expect(
|
||||
file.errors.errors,
|
||||
contains(isA<DriftError>().having(
|
||||
(e) => e.message,
|
||||
'message',
|
||||
contains('Could not find t.'),
|
||||
)));
|
||||
});
|
||||
|
||||
test('does not allow nested columns', () async {
|
||||
final state = TestState.withContent({
|
||||
'foo|lib/a.moor': '''
|
||||
CREATE TABLE foo (bar INTEGER NOT NULL PRIMARY KEY);
|
||||
|
||||
CREATE VIEW v AS SELECT foo.** FROM foo;
|
||||
''',
|
||||
});
|
||||
|
||||
final file = await state.analyze('package:foo/a.moor');
|
||||
|
||||
state.close();
|
||||
|
||||
expect(
|
||||
file.errors.errors,
|
||||
contains(isA<DriftError>().having(
|
||||
(e) => e.message,
|
||||
'message',
|
||||
contains('Nested star columns may only appear in a top-level select '
|
||||
'query.'),
|
||||
)));
|
||||
});
|
||||
}
|
|
@ -1,149 +0,0 @@
|
|||
import 'package:drift_dev/moor_generator.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import '../utils.dart';
|
||||
|
||||
void main() {
|
||||
group('finds referenced tables', () {
|
||||
const definitions = {
|
||||
'foo|lib/b.moor': '''
|
||||
CREATE TABLE users (
|
||||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
name VARCHAR
|
||||
);
|
||||
'''
|
||||
};
|
||||
|
||||
TestState? state;
|
||||
|
||||
tearDown(() => state?.close());
|
||||
|
||||
test('in a foreign key clause', () async {
|
||||
state = TestState.withContent(const {
|
||||
'foo|lib/a.moor': '''
|
||||
import 'b.moor';
|
||||
|
||||
CREATE TABLE friendships (
|
||||
user_a INTEGER REFERENCES users (id),
|
||||
user_b INTEGER REFERENCES users (id),
|
||||
|
||||
PRIMARY KEY(user_a, user_b),
|
||||
CHECK (user_a != user_b)
|
||||
);
|
||||
''',
|
||||
...definitions,
|
||||
});
|
||||
|
||||
final file = await state!.analyze('package:foo/a.moor');
|
||||
expect(file.errors.errors, isEmpty);
|
||||
|
||||
final table = file.currentResult!.declaredTables.single;
|
||||
expect(
|
||||
table.references,
|
||||
[
|
||||
const TypeMatcher<DriftTable>()
|
||||
.having((table) => table.displayName, 'displayName', 'users'),
|
||||
],
|
||||
);
|
||||
});
|
||||
|
||||
test('in a trigger', () async {
|
||||
state = TestState.withContent(const {
|
||||
'foo|lib/a.moor': '''
|
||||
import 'b.moor';
|
||||
|
||||
CREATE TABLE friendships (
|
||||
user_a INTEGER REFERENCES users (id),
|
||||
user_b INTEGER REFERENCES users (id),
|
||||
|
||||
PRIMARY KEY(user_a, user_b),
|
||||
CHECK (user_a != user_b)
|
||||
);
|
||||
|
||||
CREATE TRIGGER my_trigger AFTER DELETE ON users BEGIN
|
||||
DELETE FROM friendships WHERE user_a = old.id OR user_b = old.id;
|
||||
END;
|
||||
''',
|
||||
...definitions,
|
||||
});
|
||||
|
||||
final file = await state!.analyze('package:foo/a.moor');
|
||||
expect(file.errors.errors, isEmpty);
|
||||
|
||||
final trigger =
|
||||
file.currentResult!.declaredEntities.whereType<MoorTrigger>().single;
|
||||
expect(
|
||||
trigger.references,
|
||||
{
|
||||
const TypeMatcher<DriftTable>().having(
|
||||
(table) => table.displayName, 'displayName', 'friendships'),
|
||||
const TypeMatcher<DriftTable>()
|
||||
.having((table) => table.displayName, 'displayName', 'users'),
|
||||
},
|
||||
);
|
||||
|
||||
expect(trigger.bodyReferences.map((t) => t.sqlName),
|
||||
{'users', 'friendships'});
|
||||
expect(trigger.bodyUpdates.map((t) => t.table.sqlName), {'friendships'});
|
||||
});
|
||||
|
||||
test('in an index', () async {
|
||||
state = TestState.withContent(const {
|
||||
'foo|lib/a.moor': '''
|
||||
import 'b.moor';
|
||||
|
||||
CREATE INDEX idx ON users (name);
|
||||
''',
|
||||
...definitions,
|
||||
});
|
||||
|
||||
final file = await state!.analyze('package:foo/a.moor');
|
||||
expect(file.errors.errors, isEmpty);
|
||||
|
||||
final trigger = file.currentResult!.declaredEntities.single as MoorIndex;
|
||||
expect(trigger.references, {
|
||||
const TypeMatcher<DriftTable>()
|
||||
.having((table) => table.displayName, 'displayName', 'users'),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
group('issues error when referencing an unknown table', () {
|
||||
TestState? state;
|
||||
|
||||
tearDown(() => state?.close());
|
||||
|
||||
test('in a foreign key clause', () async {
|
||||
state = TestState.withContent(const {
|
||||
'foo|lib/a.moor': '''
|
||||
CREATE TABLE foo (
|
||||
id INTEGER NOT NULL REFERENCES bar (baz) PRIMARY KEY
|
||||
);
|
||||
'''
|
||||
});
|
||||
|
||||
final file = await state!.analyze('package:foo/a.moor');
|
||||
|
||||
expect(
|
||||
file.errors.errors.map((e) => e.message),
|
||||
contains('Referenced table bar could not befound.'),
|
||||
);
|
||||
});
|
||||
|
||||
test('in a trigger', () async {
|
||||
state = TestState.withContent(const {
|
||||
'foo|lib/a.moor': '''
|
||||
CREATE TRIGGER IF NOT EXISTS foo BEFORE DELETE ON bar BEGIN
|
||||
END;
|
||||
''',
|
||||
});
|
||||
|
||||
final file = await state!.analyze('package:foo/a.moor');
|
||||
|
||||
expect(
|
||||
file.errors.errors.map((e) => e.message),
|
||||
contains('Target table bar could not be found.'),
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
@Tags(['analyzer'])
|
||||
import 'package:drift_dev/src/analyzer/errors.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import '../utils.dart';
|
||||
|
||||
void main() {
|
||||
test('reports an error when importing a part file into .moor', () async {
|
||||
final state = TestState.withContent({
|
||||
'a|lib/base.dart': '''
|
||||
import 'package:drift/drift.dart';
|
||||
|
||||
part 'tables.dart';
|
||||
''',
|
||||
'a|lib/tables.dart': '''
|
||||
part of 'base.dart';
|
||||
|
||||
class Events extends Table {
|
||||
IntColumn get id => integer().autoIncrement()();
|
||||
|
||||
RealColumn get sumVal => real().withDefault(Constant(0))();
|
||||
}
|
||||
|
||||
class Records extends Table {
|
||||
IntColumn get eventId => integer()();
|
||||
|
||||
RealColumn get value => real().nullable()();
|
||||
}
|
||||
''',
|
||||
'a|lib/file.moor': '''
|
||||
import 'tables.dart';
|
||||
''',
|
||||
});
|
||||
addTearDown(state.close);
|
||||
|
||||
final file = await state.analyze('package:a/file.moor');
|
||||
expect(file.errors.errors, hasLength(1));
|
||||
expect(
|
||||
file.errors.errors.single,
|
||||
isA<ErrorInDriftFile>()
|
||||
.having(
|
||||
(e) => e.message,
|
||||
'message',
|
||||
contains('Is it a part file?'),
|
||||
)
|
||||
.having((e) => e.span?.text, 'span.text', "import 'tables.dart';"),
|
||||
);
|
||||
});
|
||||
}
|
|
@ -1,83 +0,0 @@
|
|||
import 'package:build/build.dart';
|
||||
import 'package:drift_dev/src/analyzer/runner/results.dart';
|
||||
import 'package:drift_dev/src/analyzer/runner/steps.dart';
|
||||
import 'package:drift_dev/src/analyzer/session.dart';
|
||||
import 'package:drift_dev/src/model/types.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import '../../utils/test_backend.dart';
|
||||
import '../utils.dart';
|
||||
|
||||
void main() {
|
||||
const content = '''
|
||||
import 'package:my_package/some_file.dart';
|
||||
import 'relative_file.moor';
|
||||
|
||||
CREATE TABLE users(
|
||||
id INT NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
name VARCHAR NOT NULL CHECK(LENGTH(name) BETWEEN 5 AND 30),
|
||||
field BOOLEAN,
|
||||
another DATETIME,
|
||||
different_json INT JSON KEY myJsonKey
|
||||
);
|
||||
|
||||
usersWithLongName: SELECT * FROM users WHERE LENGTH(name) > 25;
|
||||
''';
|
||||
|
||||
test('parses standalone .moor files', () async {
|
||||
final asset = AssetId.parse('foo|bar.moor');
|
||||
final backend = TestBackend({asset: content});
|
||||
final session = MoorSession(backend);
|
||||
final task = session.startTask(backend.startTask(asset.uri));
|
||||
final file = session.registerFile(asset.uri);
|
||||
|
||||
final parseStep = ParseMoorStep(task, file, content);
|
||||
final result = await parseStep.parseFile();
|
||||
|
||||
expect(parseStep.errors.errors, isEmpty);
|
||||
|
||||
final table = result.declaredTables.single;
|
||||
expect(table.sqlName, 'users');
|
||||
expect(table.columns.map((c) => c.name.name),
|
||||
['id', 'name', 'field', 'another', 'different_json']);
|
||||
expect(table.columns.map((c) => c.dartGetterName),
|
||||
['id', 'name', 'field', 'another', 'differentJson']);
|
||||
expect(table.columns.map((c) => c.dartTypeCode()),
|
||||
['int', 'String', 'bool?', 'DateTime?', 'int?']);
|
||||
expect(table.columns.map((c) => c.getJsonKey()),
|
||||
['id', 'name', 'field', 'another', 'myJsonKey']);
|
||||
|
||||
backend.finish();
|
||||
});
|
||||
|
||||
test('recognizes aliases to rowid', () async {
|
||||
final state = TestState.withContent({
|
||||
'foo|lib/a.moor': '''
|
||||
CREATE TABLE users (
|
||||
id INTEGER PRIMARY KEY,
|
||||
name TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE users2 (
|
||||
id INTEGER,
|
||||
name TEXT NOT NULL,
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
'''
|
||||
});
|
||||
|
||||
final result = await state.analyze('package:foo/a.moor');
|
||||
state.close();
|
||||
final file = result.currentResult as ParsedDriftFile;
|
||||
|
||||
final users1 = file.declaredTables.singleWhere((t) => t.sqlName == 'users');
|
||||
final users2 =
|
||||
file.declaredTables.singleWhere((t) => t.sqlName == 'users2');
|
||||
|
||||
expect(users1.isColumnRequiredForInsert(users1.columns[0]), isFalse);
|
||||
expect(users1.isColumnRequiredForInsert(users1.columns[1]), isTrue);
|
||||
|
||||
expect(users2.isColumnRequiredForInsert(users2.columns[0]), isFalse);
|
||||
expect(users2.isColumnRequiredForInsert(users2.columns[1]), isTrue);
|
||||
});
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
import 'package:drift_dev/src/analyzer/errors.dart';
|
||||
import 'package:drift_dev/src/analyzer/options.dart';
|
||||
import 'package:sqlparser/sqlparser.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import '../utils.dart';
|
||||
|
||||
const _moorFile = '''
|
||||
CREATE TABLE foo (
|
||||
id INTEGER NOT NULL PRIMARY KEY,
|
||||
content TEXT NOT NULL UNIQUE,
|
||||
content2 TEXT NOT NULL UNIQUE
|
||||
);
|
||||
|
||||
query: INSERT INTO foo VALUES (?, ?, ?)
|
||||
ON CONFLICT (content) DO NOTHING
|
||||
ON CONFLICT (content2) DO UPDATE SET content2 = 'duplicate';
|
||||
''';
|
||||
|
||||
void main() {
|
||||
test('does not support newer sqlite features by default', () async {
|
||||
final state = TestState.withContent(
|
||||
const {
|
||||
'a|lib/main.moor': _moorFile,
|
||||
},
|
||||
);
|
||||
|
||||
final file = await state.analyze('package:a/main.moor');
|
||||
expect(file.errors.errors, hasLength(1));
|
||||
expect(
|
||||
file.errors.errors.single,
|
||||
isA<ErrorInDriftFile>().having(
|
||||
(e) => e.message,
|
||||
'message',
|
||||
allOf(
|
||||
contains('require sqlite version 3.35 or later'),
|
||||
contains(
|
||||
'You can change the assumed sqlite version with build options.'),
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
test('supports newer sqlite features', () async {
|
||||
final state = TestState.withContent(
|
||||
const {
|
||||
'a|lib/main.moor': _moorFile,
|
||||
},
|
||||
options: const DriftOptions.defaults(
|
||||
sqliteAnalysisOptions: SqliteAnalysisOptions(
|
||||
version: SqliteVersion.v3_35,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final file = await state.analyze('package:a/main.moor');
|
||||
expect(file.errors.errors, isEmpty);
|
||||
});
|
||||
}
|
Loading…
Reference in New Issue