<commit_before>import 'package:moor/moor.dart' hide isNull;
import 'package:test/test.dart';
import 'data/tables/todos.dart';
import 'data/utils/mocks.dart';

void main() {
  TodoDb db;
  MockExecutor executor;

  setUp(() {
    executor = MockExecutor();
    db = TodoDb(executor);
  });

  test('generates join statements', () async {
    final todos = db.alias(db.todosTable, 't');
    final categories = db.alias(db.categories, 'c');

    await db.select(todos).join([
      leftOuterJoin(categories, categories.id.equalsExp(todos.category))
    ]).get();

    verify(executor.runSelect(
        'SELECT t.id AS "t.id", t.title AS "t.title", '
        't.content AS "t.content", t.target_date AS "t.target_date", '
        't.category AS "t.category", c.id AS "c.id", c.`desc` AS "c.desc", '
        'c.priority AS "c.priority" '
        'FROM todos t LEFT OUTER JOIN categories c ON c.id = t.category;',
        argThat(isEmpty)));
  });

  test('parses results from multiple tables', () async {
    final todos = db.alias(db.todosTable, 't');
    final categories = db.alias(db.categories, 'c');

    final date = DateTime(2019, 03, 20);
    when(executor.runSelect(any, any)).thenAnswer((_) {
      return Future.value([
        {
          't.id': 5,
          't.title': 'title',
          't.content': 'content',
          't.target_date': date.millisecondsSinceEpoch ~/ 1000,
          't.category': 3,
          'c.id': 3,
          'c.desc': 'description',
          'c.priority': 2,
        }
      ]);
    });

    final result = await db.select(todos, distinct: true).join([
      leftOuterJoin(categories, categories.id.equalsExp(todos.category))
    ]).get();

    expect(result, hasLength(1));

    final row = result.single;
    expect(
        row.readTable(todos),
        TodoEntry(
          id: 5,
          title: 'title',
          content: 'content',
          targetDate: date,
          category: 3,
        ));

    expect(
      row.readTable(categories),
      Category(
        id: 3,
        description: 'description',
        priority: CategoryPriority.high,
      ),
    );

    verify(executor.runSelect(argThat(contains('DISTINCT')), any));
  });

  test('reports null when no data is available', () async {
    when(executor.runSelect(any, any)).thenAnswer((_) {
      return Future.value([
        {
          'todos.id': 5,
          'todos.title': 'title',
          'todos.content': 'content',
          'todos.target_date': null,
          'todos.category': null,
        }
      ]);
    });

    final result = await db.select(db.todosTable).join([
      leftOuterJoin(
          db.categories, db.categories.id.equalsExp(db.todosTable.category))
    ]).get();

    expect(result, hasLength(1));

    final row = result.single;
    expect(row.readTable(db.categories), null);
    expect(
        row.readTable(db.todosTable),
        TodoEntry(
          id: 5,
          title: 'title',
          content: 'content',
        ));
  });

  test('where and order-by clauses are kept', () async {
    final todos = db.alias(db.todosTable, 't');
    final categories = db.alias(db.categories, 'c');

    final normalQuery = db.select(todos)
      ..where((t) => t.id.isSmallerThanValue(3))
      ..orderBy([(t) => OrderingTerm(expression: t.title)]);

    await normalQuery.join(
        [innerJoin(categories, categories.id.equalsExp(todos.category))]).get();

    verify(executor.runSelect(
        argThat(contains('WHERE t.id < ? ORDER BY t.title ASC')), [3]));
  });

  test('limit clause is kept', () async {
    final todos = db.alias(db.todosTable, 't');
    final categories = db.alias(db.categories, 'c');

    final normalQuery = db.select(todos)..limit(10, offset: 5);

    await normalQuery.join(
        [innerJoin(categories, categories.id.equalsExp(todos.category))]).get();

    verify(executor.runSelect(argThat(contains('LIMIT 10 OFFSET 5')), []));
  });

  test('can be watched', () {
    final todos = db.alias(db.todosTable, 't');
    final categories = db.alias(db.categories, 'c');

    final query = db
        .select(todos)
        .join([innerJoin(categories, todos.category.equalsExp(categories.id))]);

    final stream = query.watch();
    expectLater(stream, emitsInOrder([[], []]));

    db.markTablesUpdated({todos});
    db.markTablesUpdated({categories});
  });

  test('updates when any queried table changes', () {
    // Nonsense query, repro for https://github.com/simolus3/moor/issues/910
    final a = db.users;
    final b = db.categories;
    final c = db.sharedTodos;

    final query = (db.selectOnly(a)..where(c.todo.isNull())).join([
      leftOuterJoin(b, b.id.equalsExp(a.id)),
      leftOuterJoin(c, c.todo.equalsExp(b.id))
    ])
      ..addColumns([b.description])
      ..groupBy([b.description]);

    final stream = query.watch();
    expectLater(stream, emitsInOrder([[], []]));

    db.markTablesUpdated({b});
  });

  test('setting where multiple times forms conjunction', () async {
    final todos = db.alias(db.todosTable, 't');
    final categories = db.alias(db.categories, 'c');

    final query = db
        .select(todos)
        .join([innerJoin(categories, todos.category.equalsExp(categories.id))])
          ..where(todos.id.isSmallerThanValue(5))
          ..where(categories.id.isBiggerOrEqualValue(10));

    await query.get();<commit_msg>Another tests for streams and migrations<commit_after>import 'package:moor/moor.dart' hide isNull;
import 'package:test/test.dart';
import 'data/tables/todos.dart';
import 'data/utils/mocks.dart';

void main() {
  TodoDb db;
  MockExecutor executor;

  setUp(() {
    executor = MockExecutor();
    db = TodoDb(executor);
  });

  test('generates join statements', () async {
    final todos = db.alias(db.todosTable, 't');
    final categories = db.alias(db.categories, 'c');

    await db.select(todos).join([
      leftOuterJoin(categories, categories.id.equalsExp(todos.category))
    ]).get();

    verify(executor.runSelect(
        'SELECT t.id AS "t.id", t.title AS "t.title", '
        't.content AS "t.content", t.target_date AS "t.target_date", '
        't.category AS "t.category", c.id AS "c.id", c.`desc` AS "c.desc", '
        'c.priority AS "c.priority" '
        'FROM todos t LEFT OUTER JOIN categories c ON c.id = t.category;',
        argThat(isEmpty)));
  });

  test('parses results from multiple tables', () async {
    final todos = db.alias(db.todosTable, 't');
    final categories = db.alias(db.categories, 'c');

    final date = DateTime(2019, 03, 20);
    when(executor.runSelect(any, any)).thenAnswer((_) {
      return Future.value([
        {
          't.id': 5,
          't.title': 'title',
          't.content': 'content',
          't.target_date': date.millisecondsSinceEpoch ~/ 1000,
          't.category': 3,
          'c.id': 3,
          'c.desc': 'description',
          'c.priority': 2,
        }
      ]);
    });

    final result = await db.select(todos, distinct: true).join([
      leftOuterJoin(categories, categories.id.equalsExp(todos.category))
    ]).get();

    expect(result, hasLength(1));

    final row = result.single;
    expect(
        row.readTable(todos),
        TodoEntry(
          id: 5,
          title: 'title',
          content: 'content',
          targetDate: date,
          category: 3,
        ));

    expect(
      row.readTable(categories),
      Category(
        id: 3,
        description: 'description',
        priority: CategoryPriority.high,
      ),
    );

    verify(executor.runSelect(argThat(contains('DISTINCT')), any));
  });

  test('reports null when no data is available', () async {
    when(executor.runSelect(any, any)).thenAnswer((_) {
      return Future.value([
        {
          'todos.id': 5,
          'todos.title': 'title',
          'todos.content': 'content',
          'todos.target_date': null,
          'todos.category': null,
        }
      ]);
    });

    final result = await db.select(db.todosTable).join([
      leftOuterJoin(
          db.categories, db.categories.id.equalsExp(db.todosTable.category))
    ]).get();

    expect(result, hasLength(1));

    final row = result.single;
    expect(row.readTable(db.categories), null);
    expect(
        row.readTable(db.todosTable),
        TodoEntry(
          id: 5,
          title: 'title',
          content: 'content',
        ));
  });

  test('where and order-by clauses are kept', () async {
    final todos = db.alias(db.todosTable, 't');
    final categories = db.alias(db.categories, 'c');

    final normalQuery = db.select(todos)
      ..where((t) => t.id.isSmallerThanValue(3))
      ..orderBy([(t) => OrderingTerm(expression: t.title)]);

    await normalQuery.join(
        [innerJoin(categories, categories.id.equalsExp(todos.category))]).get();

    verify(executor.runSelect(
        argThat(contains('WHERE t.id < ? ORDER BY t.title ASC')), [3]));
  });

  test('limit clause is kept', () async {
    final todos = db.alias(db.todosTable, 't');
    final categories = db.alias(db.categories, 'c');

    final normalQuery = db.select(todos)..limit(10, offset: 5);

    await normalQuery.join(
        [innerJoin(categories, categories.id.equalsExp(todos.category))]).get();

    verify(executor.runSelect(argThat(contains('LIMIT 10 OFFSET 5')), []));
  });

  test('can be watched', () {
    final todos = db.alias(db.todosTable, 't');
    final categories = db.alias(db.categories, 'c');

    final query = db
        .select(todos)
        .join([innerJoin(categories, todos.category.equalsExp(categories.id))]);

    final stream = query.watch();
    expectLater(stream, emitsInOrder([[], []]));

    db.markTablesUpdated({todos});
    db.markTablesUpdated({categories});
  });

  test('updates when any queried table changes in transaction', () {
    // Nonsense query, repro for https://github.com/simolus3/moor/issues/910
    final a = db.users;
    final b = db.categories;
    final c = db.sharedTodos;

    final query = (db.selectOnly(a)..where(c.todo.isNull())).join([
      leftOuterJoin(b, b.id.equalsExp(a.id)),
      leftOuterJoin(c, c.todo.equalsExp(b.id))
    ])
      ..addColumns([b.description])
      ..groupBy([b.description]);

    final stream = query.watch();
    expectLater(stream, emitsInOrder([[], []]));

    return db.transaction(() async {
      db.markTablesUpdated({b});
    });
  });

  test('setting where multiple times forms conjunction', () async {
    final todos = db.alias(db.todosTable, 't');
    final categories = db.alias(db.categories, 'c');

    final query = db
        .select(todos)
        .join([innerJoin(categories, todos.category.equalsExp(categories.id))])
          ..where(todos.id.isSmallerThanValue(5))
          ..where(categories.id.isBiggerOrEqualValue(10));

    await query.get();
{'fraction_non_alphanumeric': 0.1646143875567077, 'fraction_numerical': 0.007036385519859272, 'mean_word_length': 2.661694915254237, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 7, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6578809', 'n_tokens_mistral': 3645, 'n_tokens_neox': 3474, 'n_words': 699}