server/tests: add func.tags tests
This commit is contained in:
		
							parent
							
								
									53e96ba41f
								
							
						
					
					
						commit
						c23c401c4d
					
				@ -238,12 +238,14 @@ def update_tag_names(tag, names):
 | 
			
		||||
        if not _check_name_intersection_case_sensitive(_get_plain_names(tag), [name]):
 | 
			
		||||
            tag.names.append(db.TagName(name))
 | 
			
		||||
 | 
			
		||||
# TODO: what to do with relations that do not yet exist?
 | 
			
		||||
def update_tag_implications(tag, relations):
 | 
			
		||||
    assert tag
 | 
			
		||||
    if _check_name_intersection(_get_plain_names(tag), relations):
 | 
			
		||||
        raise InvalidTagRelationError('Tag cannot imply itself.')
 | 
			
		||||
    tag.implications = get_tags_by_names(relations)
 | 
			
		||||
 | 
			
		||||
# TODO: what to do with relations that do not yet exist?
 | 
			
		||||
def update_tag_suggestions(tag, relations):
 | 
			
		||||
    assert tag
 | 
			
		||||
    if _check_name_intersection(_get_plain_names(tag), relations):
 | 
			
		||||
 | 
			
		||||
@ -120,49 +120,6 @@ def test_omitting_optional_field(test_ctx, field):
 | 
			
		||||
            user=test_ctx.user_factory(rank=db.User.RANK_REGULAR)))
 | 
			
		||||
    assert result is not None
 | 
			
		||||
 | 
			
		||||
def test_duplicating_names(test_ctx):
 | 
			
		||||
    result = test_ctx.api.post(
 | 
			
		||||
        test_ctx.context_factory(
 | 
			
		||||
            input={
 | 
			
		||||
                'names': ['tag1', 'TAG1'],
 | 
			
		||||
                'category': 'meta',
 | 
			
		||||
                'suggestions': [],
 | 
			
		||||
                'implications': [],
 | 
			
		||||
            },
 | 
			
		||||
            user=test_ctx.user_factory(rank=db.User.RANK_REGULAR)))
 | 
			
		||||
    assert result['names'] == ['tag1']
 | 
			
		||||
    assert result['category'] == 'meta'
 | 
			
		||||
    tag = tags.get_tag_by_name('tag1')
 | 
			
		||||
    assert [tag_name.name for tag_name in tag.names] == ['tag1']
 | 
			
		||||
 | 
			
		||||
def test_trying_to_use_existing_name(test_ctx):
 | 
			
		||||
    db.session.add_all([
 | 
			
		||||
        test_ctx.tag_factory(names=['used1']),
 | 
			
		||||
        test_ctx.tag_factory(names=['used2']),
 | 
			
		||||
    ])
 | 
			
		||||
    db.session.commit()
 | 
			
		||||
    with pytest.raises(tags.TagAlreadyExistsError):
 | 
			
		||||
        test_ctx.api.post(
 | 
			
		||||
            test_ctx.context_factory(
 | 
			
		||||
                input={
 | 
			
		||||
                    'names': ['used1', 'unused'],
 | 
			
		||||
                    'category': 'meta',
 | 
			
		||||
                    'suggestions': [],
 | 
			
		||||
                    'implications': [],
 | 
			
		||||
                },
 | 
			
		||||
                user=test_ctx.user_factory(rank=db.User.RANK_REGULAR)))
 | 
			
		||||
    with pytest.raises(tags.TagAlreadyExistsError):
 | 
			
		||||
        test_ctx.api.post(
 | 
			
		||||
            test_ctx.context_factory(
 | 
			
		||||
                input={
 | 
			
		||||
                    'names': ['USED2', 'unused'],
 | 
			
		||||
                    'category': 'meta',
 | 
			
		||||
                    'suggestions': [],
 | 
			
		||||
                    'implications': [],
 | 
			
		||||
                },
 | 
			
		||||
                user=test_ctx.user_factory(rank=db.User.RANK_REGULAR)))
 | 
			
		||||
    assert tags.try_get_tag_by_name('unused') is None
 | 
			
		||||
 | 
			
		||||
def test_creating_new_category(test_ctx):
 | 
			
		||||
    with pytest.raises(tag_categories.TagCategoryNotFoundError):
 | 
			
		||||
        test_ctx.api.post(
 | 
			
		||||
@ -217,51 +174,6 @@ def test_creating_new_suggestions_and_implications(
 | 
			
		||||
    for name in ['main'] + expected_suggestions + expected_implications:
 | 
			
		||||
        assert tags.try_get_tag_by_name(name) is not None
 | 
			
		||||
 | 
			
		||||
def test_reusing_suggestions_and_implications(test_ctx):
 | 
			
		||||
    db.session.add_all([
 | 
			
		||||
        test_ctx.tag_factory(names=['tag1', 'tag2']),
 | 
			
		||||
        test_ctx.tag_factory(names=['tag3']),
 | 
			
		||||
    ])
 | 
			
		||||
    db.session.commit()
 | 
			
		||||
    result = test_ctx.api.post(
 | 
			
		||||
        test_ctx.context_factory(
 | 
			
		||||
            input={
 | 
			
		||||
                'names': ['new'],
 | 
			
		||||
                'category': 'meta',
 | 
			
		||||
                'suggestions': ['TAG2'],
 | 
			
		||||
                'implications': ['tag1'],
 | 
			
		||||
            },
 | 
			
		||||
            user=test_ctx.user_factory(rank=db.User.RANK_REGULAR)))
 | 
			
		||||
    # NOTE: it should export only the first name
 | 
			
		||||
    assert result['suggestions'] == ['tag1']
 | 
			
		||||
    assert result['implications'] == ['tag1']
 | 
			
		||||
    tag = tags.get_tag_by_name('new')
 | 
			
		||||
    assert_relations(tag.suggestions, ['tag1'])
 | 
			
		||||
    assert_relations(tag.implications, ['tag1'])
 | 
			
		||||
 | 
			
		||||
@pytest.mark.parametrize('input', [
 | 
			
		||||
    {
 | 
			
		||||
        'names': ['tag'],
 | 
			
		||||
        'category': 'meta',
 | 
			
		||||
        'suggestions': ['tag'],
 | 
			
		||||
        'implications': [],
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        'names': ['tag'],
 | 
			
		||||
        'category': 'meta',
 | 
			
		||||
        'suggestions': [],
 | 
			
		||||
        'implications': ['tag'],
 | 
			
		||||
    }
 | 
			
		||||
])
 | 
			
		||||
def test_tag_trying_to_relate_to_itself(test_ctx, input):
 | 
			
		||||
    with pytest.raises(tags.TagAlreadyExistsError):
 | 
			
		||||
        test_ctx.api.post(
 | 
			
		||||
            test_ctx.context_factory(
 | 
			
		||||
                input=input,
 | 
			
		||||
                user=test_ctx.user_factory(rank=db.User.RANK_REGULAR)))
 | 
			
		||||
    db.session.rollback()
 | 
			
		||||
    assert tags.try_get_tag_by_name('tag') is None
 | 
			
		||||
 | 
			
		||||
def test_trying_to_create_tag_without_privileges(test_ctx):
 | 
			
		||||
    with pytest.raises(errors.AuthError):
 | 
			
		||||
        test_ctx.api.post(
 | 
			
		||||
 | 
			
		||||
@ -32,21 +32,6 @@ def test_deleting(test_ctx):
 | 
			
		||||
    assert db.session.query(db.Tag).count() == 0
 | 
			
		||||
    assert os.path.exists(os.path.join(config.config['data_dir'], 'tags.json'))
 | 
			
		||||
 | 
			
		||||
def test_deleting_used(test_ctx, post_factory):
 | 
			
		||||
    tag = test_ctx.tag_factory(names=['tag'])
 | 
			
		||||
    post = post_factory()
 | 
			
		||||
    post.tags.append(tag)
 | 
			
		||||
    db.session.add_all([tag, post])
 | 
			
		||||
    db.session.commit()
 | 
			
		||||
    test_ctx.api.delete(
 | 
			
		||||
        test_ctx.context_factory(
 | 
			
		||||
            input={'version': 1},
 | 
			
		||||
            user=test_ctx.user_factory(rank=db.User.RANK_REGULAR)),
 | 
			
		||||
        'tag')
 | 
			
		||||
    db.session.refresh(post)
 | 
			
		||||
    assert db.session.query(db.Tag).count() == 0
 | 
			
		||||
    assert post.tags == []
 | 
			
		||||
 | 
			
		||||
def test_trying_to_delete_non_existing(test_ctx):
 | 
			
		||||
    with pytest.raises(tags.TagNotFoundError):
 | 
			
		||||
        test_ctx.api.delete(
 | 
			
		||||
 | 
			
		||||
@ -1,64 +0,0 @@
 | 
			
		||||
import datetime
 | 
			
		||||
import os
 | 
			
		||||
import json
 | 
			
		||||
from szurubooru import config, db
 | 
			
		||||
from szurubooru.func import tags
 | 
			
		||||
 | 
			
		||||
def test_export(
 | 
			
		||||
        tmpdir,
 | 
			
		||||
        query_counter,
 | 
			
		||||
        config_injector,
 | 
			
		||||
        post_factory,
 | 
			
		||||
        tag_factory,
 | 
			
		||||
        tag_category_factory):
 | 
			
		||||
    config_injector({
 | 
			
		||||
        'data_dir': str(tmpdir)
 | 
			
		||||
    })
 | 
			
		||||
    cat1 = tag_category_factory(name='cat1', color='black')
 | 
			
		||||
    cat2 = tag_category_factory(name='cat2', color='white')
 | 
			
		||||
    db.session.add_all([cat1, cat2])
 | 
			
		||||
    db.session.flush()
 | 
			
		||||
    sug1 = tag_factory(names=['sug1'], category=cat1)
 | 
			
		||||
    sug2 = tag_factory(names=['sug2'], category=cat1)
 | 
			
		||||
    imp1 = tag_factory(names=['imp1'], category=cat1)
 | 
			
		||||
    imp2 = tag_factory(names=['imp2'], category=cat1)
 | 
			
		||||
    tag = tag_factory(names=['alias1', 'alias2'], category=cat2)
 | 
			
		||||
    db.session.add_all([tag, sug1, sug2, imp1, imp2, cat1, cat2])
 | 
			
		||||
    post = post_factory()
 | 
			
		||||
    post.tags = [tag]
 | 
			
		||||
    db.session.flush()
 | 
			
		||||
    db.session.add_all([
 | 
			
		||||
        post,
 | 
			
		||||
        db.TagSuggestion(tag.tag_id, sug1.tag_id),
 | 
			
		||||
        db.TagSuggestion(tag.tag_id, sug2.tag_id),
 | 
			
		||||
        db.TagImplication(tag.tag_id, imp1.tag_id),
 | 
			
		||||
        db.TagImplication(tag.tag_id, imp2.tag_id),
 | 
			
		||||
    ])
 | 
			
		||||
    db.session.flush()
 | 
			
		||||
 | 
			
		||||
    with query_counter:
 | 
			
		||||
        tags.export_to_json()
 | 
			
		||||
        assert len(query_counter.statements) == 5
 | 
			
		||||
 | 
			
		||||
    export_path = os.path.join(config.config['data_dir'], 'tags.json')
 | 
			
		||||
    assert os.path.exists(export_path)
 | 
			
		||||
    with open(export_path, 'r') as handle:
 | 
			
		||||
        assert json.loads(handle.read()) == {
 | 
			
		||||
            'tags': [
 | 
			
		||||
                {
 | 
			
		||||
                    'names': ['alias1', 'alias2'],
 | 
			
		||||
                    'usages': 1,
 | 
			
		||||
                    'category': 'cat2',
 | 
			
		||||
                    'suggestions': ['sug1', 'sug2'],
 | 
			
		||||
                    'implications': ['imp1', 'imp2'],
 | 
			
		||||
                },
 | 
			
		||||
                {'names': ['sug1'], 'usages': 0, 'category': 'cat1'},
 | 
			
		||||
                {'names': ['sug2'], 'usages': 0, 'category': 'cat1'},
 | 
			
		||||
                {'names': ['imp1'], 'usages': 0, 'category': 'cat1'},
 | 
			
		||||
                {'names': ['imp2'], 'usages': 0, 'category': 'cat1'},
 | 
			
		||||
            ],
 | 
			
		||||
            'categories': [
 | 
			
		||||
                {'name': 'cat1', 'color': 'black'},
 | 
			
		||||
                {'name': 'cat2', 'color': 'white'},
 | 
			
		||||
            ]
 | 
			
		||||
        }
 | 
			
		||||
@ -26,40 +26,6 @@ def test_ctx(
 | 
			
		||||
    ret.api = api.TagMergeApi()
 | 
			
		||||
    return ret
 | 
			
		||||
 | 
			
		||||
def test_merging_without_usages(test_ctx, fake_datetime):
 | 
			
		||||
    category = test_ctx.tag_category_factory(name='meta')
 | 
			
		||||
    source_tag = test_ctx.tag_factory(names=['source'])
 | 
			
		||||
    target_tag = test_ctx.tag_factory(names=['target'], category=category)
 | 
			
		||||
    db.session.add_all([source_tag, target_tag])
 | 
			
		||||
    db.session.commit()
 | 
			
		||||
    with fake_datetime('1997-12-01'):
 | 
			
		||||
        result = test_ctx.api.post(
 | 
			
		||||
            test_ctx.context_factory(
 | 
			
		||||
                input={
 | 
			
		||||
                    'removeVersion': 1,
 | 
			
		||||
                    'mergeToVersion': 1,
 | 
			
		||||
                    'remove': 'source',
 | 
			
		||||
                    'mergeTo': 'target',
 | 
			
		||||
                },
 | 
			
		||||
                user=test_ctx.user_factory(rank=db.User.RANK_REGULAR)))
 | 
			
		||||
    assert 'snapshots' in result
 | 
			
		||||
    del result['snapshots']
 | 
			
		||||
    assert result == {
 | 
			
		||||
        'names': ['target'],
 | 
			
		||||
        'category': 'meta',
 | 
			
		||||
        'description': None,
 | 
			
		||||
        'suggestions': [],
 | 
			
		||||
        'implications': [],
 | 
			
		||||
        'creationTime': datetime.datetime(1996, 1, 1),
 | 
			
		||||
        'lastEditTime': None,
 | 
			
		||||
        'usages': 0,
 | 
			
		||||
        'version': 2,
 | 
			
		||||
    }
 | 
			
		||||
    assert tags.try_get_tag_by_name('source') is None
 | 
			
		||||
    tag = tags.get_tag_by_name('target')
 | 
			
		||||
    assert tag is not None
 | 
			
		||||
    assert os.path.exists(os.path.join(config.config['data_dir'], 'tags.json'))
 | 
			
		||||
 | 
			
		||||
def test_merging_with_usages(test_ctx, fake_datetime, post_factory):
 | 
			
		||||
    source_tag = test_ctx.tag_factory(names=['source'])
 | 
			
		||||
    target_tag = test_ctx.tag_factory(names=['target'])
 | 
			
		||||
@ -86,83 +52,6 @@ def test_merging_with_usages(test_ctx, fake_datetime, post_factory):
 | 
			
		||||
    assert tags.try_get_tag_by_name('source') is None
 | 
			
		||||
    assert tags.get_tag_by_name('target').post_count == 1
 | 
			
		||||
 | 
			
		||||
def test_merging_when_related(test_ctx, fake_datetime):
 | 
			
		||||
    source_tag = test_ctx.tag_factory(names=['source'])
 | 
			
		||||
    target_tag = test_ctx.tag_factory(names=['target'])
 | 
			
		||||
    db.session.add_all([source_tag, target_tag])
 | 
			
		||||
    db.session.flush()
 | 
			
		||||
    referring_tag = test_ctx.tag_factory(names=['parent'])
 | 
			
		||||
    referring_tag.suggestions = [source_tag]
 | 
			
		||||
    referring_tag.implications = [source_tag]
 | 
			
		||||
    db.session.add(referring_tag)
 | 
			
		||||
    db.session.commit()
 | 
			
		||||
    assert tags.try_get_tag_by_name('parent').implications != []
 | 
			
		||||
    assert tags.try_get_tag_by_name('parent').suggestions != []
 | 
			
		||||
    with fake_datetime('1997-12-01'):
 | 
			
		||||
        result = test_ctx.api.post(
 | 
			
		||||
            test_ctx.context_factory(
 | 
			
		||||
                input={
 | 
			
		||||
                    'removeVersion': 1,
 | 
			
		||||
                    'mergeToVersion': 1,
 | 
			
		||||
                    'remove': 'source',
 | 
			
		||||
                    'mergeTo': 'target',
 | 
			
		||||
                },
 | 
			
		||||
                user=test_ctx.user_factory(rank=db.User.RANK_REGULAR)))
 | 
			
		||||
    assert tags.try_get_tag_by_name('source') is None
 | 
			
		||||
    assert tags.try_get_tag_by_name('parent').implications == []
 | 
			
		||||
    assert tags.try_get_tag_by_name('parent').suggestions == []
 | 
			
		||||
 | 
			
		||||
def test_merging_when_target_exists(test_ctx, fake_datetime, post_factory):
 | 
			
		||||
    source_tag = test_ctx.tag_factory(names=['source'])
 | 
			
		||||
    target_tag = test_ctx.tag_factory(names=['target'])
 | 
			
		||||
    db.session.add_all([source_tag, target_tag])
 | 
			
		||||
    db.session.flush()
 | 
			
		||||
    post1 = post_factory()
 | 
			
		||||
    post1.tags = [source_tag, target_tag]
 | 
			
		||||
    db.session.add_all([post1])
 | 
			
		||||
    db.session.commit()
 | 
			
		||||
    assert source_tag.post_count == 1
 | 
			
		||||
    assert target_tag.post_count == 1
 | 
			
		||||
    with fake_datetime('1997-12-01'):
 | 
			
		||||
        result = test_ctx.api.post(
 | 
			
		||||
            test_ctx.context_factory(
 | 
			
		||||
                input={
 | 
			
		||||
                    'removeVersion': 1,
 | 
			
		||||
                    'mergeToVersion': 1,
 | 
			
		||||
                    'remove': 'source',
 | 
			
		||||
                    'mergeTo': 'target',
 | 
			
		||||
                },
 | 
			
		||||
                user=test_ctx.user_factory(rank=db.User.RANK_REGULAR)))
 | 
			
		||||
    assert tags.try_get_tag_by_name('source') is None
 | 
			
		||||
    assert tags.get_tag_by_name('target').post_count == 1
 | 
			
		||||
 | 
			
		||||
@pytest.mark.parametrize('input,expected_exception', [
 | 
			
		||||
    ({'remove': None}, tags.TagNotFoundError),
 | 
			
		||||
    ({'remove': ''}, tags.TagNotFoundError),
 | 
			
		||||
    ({'remove': []}, tags.TagNotFoundError),
 | 
			
		||||
    ({'mergeTo': None}, tags.TagNotFoundError),
 | 
			
		||||
    ({'mergeTo': ''}, tags.TagNotFoundError),
 | 
			
		||||
    ({'mergeTo': []}, tags.TagNotFoundError),
 | 
			
		||||
])
 | 
			
		||||
def test_trying_to_pass_invalid_input(test_ctx, input, expected_exception):
 | 
			
		||||
    source_tag = test_ctx.tag_factory(names=['source'])
 | 
			
		||||
    target_tag = test_ctx.tag_factory(names=['target'])
 | 
			
		||||
    db.session.add_all([source_tag, target_tag])
 | 
			
		||||
    db.session.commit()
 | 
			
		||||
    real_input = {
 | 
			
		||||
        'removeVersion': 1,
 | 
			
		||||
        'mergeToVersion': 1,
 | 
			
		||||
        'remove': 'source',
 | 
			
		||||
        'mergeTo': 'target',
 | 
			
		||||
    }
 | 
			
		||||
    for key, value in input.items():
 | 
			
		||||
        real_input[key] = value
 | 
			
		||||
    with pytest.raises(expected_exception):
 | 
			
		||||
        test_ctx.api.post(
 | 
			
		||||
            test_ctx.context_factory(
 | 
			
		||||
                input=real_input,
 | 
			
		||||
                user=test_ctx.user_factory(rank=db.User.RANK_REGULAR)))
 | 
			
		||||
 | 
			
		||||
@pytest.mark.parametrize(
 | 
			
		||||
    'field', ['remove', 'mergeTo', 'removeVersion', 'mergeToVersion'])
 | 
			
		||||
def test_trying_to_omit_mandatory_field(test_ctx, field):
 | 
			
		||||
@ -198,19 +87,6 @@ def test_trying_to_merge_non_existing(test_ctx):
 | 
			
		||||
                input={'remove': 'bad', 'mergeTo': 'good'},
 | 
			
		||||
                user=test_ctx.user_factory(rank=db.User.RANK_REGULAR)))
 | 
			
		||||
 | 
			
		||||
def test_trying_to_merge_to_itself(test_ctx):
 | 
			
		||||
    db.session.add(test_ctx.tag_factory(names=['good']))
 | 
			
		||||
    db.session.commit()
 | 
			
		||||
    with pytest.raises(tags.InvalidTagRelationError):
 | 
			
		||||
        test_ctx.api.post(
 | 
			
		||||
            test_ctx.context_factory(
 | 
			
		||||
                input={
 | 
			
		||||
                    'removeVersion': 1,
 | 
			
		||||
                    'mergeToVersion': 1,
 | 
			
		||||
                    'remove': 'good',
 | 
			
		||||
                    'mergeTo': 'good'},
 | 
			
		||||
                user=test_ctx.user_factory(rank=db.User.RANK_REGULAR)))
 | 
			
		||||
 | 
			
		||||
@pytest.mark.parametrize('input', [
 | 
			
		||||
    {'names': 'whatever'},
 | 
			
		||||
    {'category': 'whatever'},
 | 
			
		||||
 | 
			
		||||
@ -28,23 +28,6 @@ def test_ctx(
 | 
			
		||||
    ret.api = api.TagSiblingsApi()
 | 
			
		||||
    return ret
 | 
			
		||||
 | 
			
		||||
def test_unused(test_ctx):
 | 
			
		||||
    db.session.add(test_ctx.tag_factory(names=['tag']))
 | 
			
		||||
    result = test_ctx.api.get(
 | 
			
		||||
        test_ctx.context_factory(
 | 
			
		||||
            user=test_ctx.user_factory(rank=db.User.RANK_REGULAR)), 'tag')
 | 
			
		||||
    assert_results(result, [])
 | 
			
		||||
 | 
			
		||||
def test_used_alone(test_ctx):
 | 
			
		||||
    tag = test_ctx.tag_factory(names=['tag'])
 | 
			
		||||
    post = test_ctx.post_factory()
 | 
			
		||||
    post.tags = [tag]
 | 
			
		||||
    db.session.add_all([post, tag])
 | 
			
		||||
    result = test_ctx.api.get(
 | 
			
		||||
        test_ctx.context_factory(
 | 
			
		||||
            user=test_ctx.user_factory(rank=db.User.RANK_REGULAR)), 'tag')
 | 
			
		||||
    assert_results(result, [])
 | 
			
		||||
 | 
			
		||||
def test_used_with_others(test_ctx):
 | 
			
		||||
    tag1 = test_ctx.tag_factory(names=['tag1'])
 | 
			
		||||
    tag2 = test_ctx.tag_factory(names=['tag2'])
 | 
			
		||||
@ -60,33 +43,6 @@ def test_used_with_others(test_ctx):
 | 
			
		||||
            user=test_ctx.user_factory(rank=db.User.RANK_REGULAR)), 'tag2')
 | 
			
		||||
    assert_results(result, [('tag1', 1)])
 | 
			
		||||
 | 
			
		||||
def test_used_with_multiple_others(test_ctx):
 | 
			
		||||
    tag1 = test_ctx.tag_factory(names=['tag1'])
 | 
			
		||||
    tag2 = test_ctx.tag_factory(names=['tag2'])
 | 
			
		||||
    tag3 = test_ctx.tag_factory(names=['tag3'])
 | 
			
		||||
    post1 = test_ctx.post_factory()
 | 
			
		||||
    post2 = test_ctx.post_factory()
 | 
			
		||||
    post3 = test_ctx.post_factory()
 | 
			
		||||
    post4 = test_ctx.post_factory()
 | 
			
		||||
    post1.tags = [tag1, tag2, tag3]
 | 
			
		||||
    post2.tags = [tag1, tag3]
 | 
			
		||||
    post3.tags = [tag2]
 | 
			
		||||
    post4.tags = [tag2]
 | 
			
		||||
    db.session.add_all([post1, post2, post3, post4, tag1, tag2, tag3])
 | 
			
		||||
    result = test_ctx.api.get(
 | 
			
		||||
        test_ctx.context_factory(
 | 
			
		||||
            user=test_ctx.user_factory(rank=db.User.RANK_REGULAR)), 'tag1')
 | 
			
		||||
    assert_results(result, [('tag3', 2), ('tag2', 1)])
 | 
			
		||||
    result = test_ctx.api.get(
 | 
			
		||||
        test_ctx.context_factory(
 | 
			
		||||
            user=test_ctx.user_factory(rank=db.User.RANK_REGULAR)), 'tag2')
 | 
			
		||||
    assert_results(result, [('tag1', 1), ('tag3', 1)])
 | 
			
		||||
    result = test_ctx.api.get(
 | 
			
		||||
        test_ctx.context_factory(
 | 
			
		||||
            user=test_ctx.user_factory(rank=db.User.RANK_REGULAR)), 'tag3')
 | 
			
		||||
    # even though tag2 is used more widely, tag1 is more relevant to tag3
 | 
			
		||||
    assert_results(result, [('tag1', 2), ('tag2', 1)])
 | 
			
		||||
 | 
			
		||||
def test_trying_to_retrieve_non_existing(test_ctx):
 | 
			
		||||
    with pytest.raises(tags.TagNotFoundError):
 | 
			
		||||
        test_ctx.api.get(
 | 
			
		||||
 | 
			
		||||
@ -124,140 +124,6 @@ def test_trying_to_update_non_existing(test_ctx):
 | 
			
		||||
                user=test_ctx.user_factory(rank=db.User.RANK_REGULAR)),
 | 
			
		||||
            'tag1')
 | 
			
		||||
 | 
			
		||||
@pytest.mark.parametrize('dup_name', ['tag1', 'TAG1'])
 | 
			
		||||
def test_reusing_own_name(test_ctx, dup_name):
 | 
			
		||||
    db.session.add(
 | 
			
		||||
        test_ctx.tag_factory(names=['tag1', 'tag2']))
 | 
			
		||||
    db.session.commit()
 | 
			
		||||
    result = test_ctx.api.put(
 | 
			
		||||
        test_ctx.context_factory(
 | 
			
		||||
            input={'names': [dup_name, 'tag3'], 'version': 1},
 | 
			
		||||
            user=test_ctx.user_factory(rank=db.User.RANK_REGULAR)),
 | 
			
		||||
        'tag1')
 | 
			
		||||
    assert result['names'] == [dup_name, 'tag3']
 | 
			
		||||
    assert tags.try_get_tag_by_name('tag2') is None
 | 
			
		||||
    tag1 = tags.get_tag_by_name('tag1')
 | 
			
		||||
    tag2 = tags.get_tag_by_name('tag3')
 | 
			
		||||
    assert tag1.tag_id == tag2.tag_id
 | 
			
		||||
    assert [name.name for name in tag1.names] == [dup_name, 'tag3']
 | 
			
		||||
 | 
			
		||||
def test_duplicating_names(test_ctx):
 | 
			
		||||
    db.session.add(
 | 
			
		||||
        test_ctx.tag_factory(names=['tag1', 'tag2']))
 | 
			
		||||
    result = test_ctx.api.put(
 | 
			
		||||
        test_ctx.context_factory(
 | 
			
		||||
            input={'names': ['tag3', 'TAG3'], 'version': 1},
 | 
			
		||||
            user=test_ctx.user_factory(rank=db.User.RANK_REGULAR)),
 | 
			
		||||
        'tag1')
 | 
			
		||||
    assert result['names'] == ['tag3']
 | 
			
		||||
    assert tags.try_get_tag_by_name('tag1') is None
 | 
			
		||||
    assert tags.try_get_tag_by_name('tag2') is None
 | 
			
		||||
    tag = tags.get_tag_by_name('tag3')
 | 
			
		||||
    assert tag is not None
 | 
			
		||||
    assert [tag_name.name for tag_name in tag.names] == ['tag3']
 | 
			
		||||
 | 
			
		||||
@pytest.mark.parametrize('dup_name', ['tag1', 'TAG1', 'tag2', 'TAG2'])
 | 
			
		||||
def test_trying_to_use_existing_name(test_ctx, dup_name):
 | 
			
		||||
    db.session.add_all([
 | 
			
		||||
        test_ctx.tag_factory(names=['tag1', 'tag2']),
 | 
			
		||||
        test_ctx.tag_factory(names=['tag3', 'tag4'])])
 | 
			
		||||
    db.session.commit()
 | 
			
		||||
    with pytest.raises(tags.TagAlreadyExistsError):
 | 
			
		||||
        test_ctx.api.put(
 | 
			
		||||
            test_ctx.context_factory(
 | 
			
		||||
                input={'names': [dup_name], 'version': 1},
 | 
			
		||||
                user=test_ctx.user_factory(rank=db.User.RANK_REGULAR)),
 | 
			
		||||
            'tag3')
 | 
			
		||||
 | 
			
		||||
@pytest.mark.parametrize('input,expected_suggestions,expected_implications', [
 | 
			
		||||
    # new relations
 | 
			
		||||
    ({
 | 
			
		||||
        'suggestions': ['sug1', 'sug2'],
 | 
			
		||||
        'implications': ['imp1', 'imp2'],
 | 
			
		||||
    }, ['sug1', 'sug2'], ['imp1', 'imp2']),
 | 
			
		||||
    # overlapping relations
 | 
			
		||||
    ({
 | 
			
		||||
        'suggestions': ['sug', 'shared'],
 | 
			
		||||
        'implications': ['shared', 'imp'],
 | 
			
		||||
    }, ['shared', 'sug'], ['imp', 'shared']),
 | 
			
		||||
    # duplicate relations
 | 
			
		||||
    ({
 | 
			
		||||
        'suggestions': ['sug', 'SUG'],
 | 
			
		||||
        'implications': ['imp', 'IMP'],
 | 
			
		||||
    }, ['sug'], ['imp']),
 | 
			
		||||
    # overlapping duplicate relations
 | 
			
		||||
    ({
 | 
			
		||||
        'suggestions': ['shared1', 'shared2'],
 | 
			
		||||
        'implications': ['SHARED1', 'SHARED2'],
 | 
			
		||||
    }, ['shared1', 'shared2'], ['shared1', 'shared2']),
 | 
			
		||||
])
 | 
			
		||||
def test_updating_new_suggestions_and_implications(
 | 
			
		||||
        test_ctx, input, expected_suggestions, expected_implications):
 | 
			
		||||
    db.session.add(
 | 
			
		||||
        test_ctx.tag_factory(names=['main']))
 | 
			
		||||
    db.session.commit()
 | 
			
		||||
    result = test_ctx.api.put(
 | 
			
		||||
        test_ctx.context_factory(
 | 
			
		||||
            input={**input, **{'version': 1}},
 | 
			
		||||
            user=test_ctx.user_factory(rank=db.User.RANK_REGULAR)),
 | 
			
		||||
        'main')
 | 
			
		||||
    assert result['suggestions'] == expected_suggestions
 | 
			
		||||
    assert result['implications'] == expected_implications
 | 
			
		||||
    tag = tags.get_tag_by_name('main')
 | 
			
		||||
    assert_relations(tag.suggestions, expected_suggestions)
 | 
			
		||||
    assert_relations(tag.implications, expected_implications)
 | 
			
		||||
    for name in ['main'] + expected_suggestions + expected_implications:
 | 
			
		||||
        assert tags.try_get_tag_by_name(name) is not None
 | 
			
		||||
 | 
			
		||||
def test_reusing_suggestions_and_implications(test_ctx):
 | 
			
		||||
    db.session.add_all([
 | 
			
		||||
        test_ctx.tag_factory(names=['tag1', 'tag2']),
 | 
			
		||||
        test_ctx.tag_factory(names=['tag3']),
 | 
			
		||||
        test_ctx.tag_factory(names=['tag4']),
 | 
			
		||||
    ])
 | 
			
		||||
    db.session.commit()
 | 
			
		||||
    result = test_ctx.api.put(
 | 
			
		||||
        test_ctx.context_factory(
 | 
			
		||||
            input={
 | 
			
		||||
                'names': ['new'],
 | 
			
		||||
                'category': 'meta',
 | 
			
		||||
                'suggestions': ['TAG2'],
 | 
			
		||||
                'implications': ['tag1'],
 | 
			
		||||
                'version': 1,
 | 
			
		||||
            },
 | 
			
		||||
            user=test_ctx.user_factory(rank=db.User.RANK_REGULAR)),
 | 
			
		||||
        'tag4')
 | 
			
		||||
    # NOTE: it should export only the first name
 | 
			
		||||
    assert result['suggestions'] == ['tag1']
 | 
			
		||||
    assert result['implications'] == ['tag1']
 | 
			
		||||
    tag = tags.get_tag_by_name('new')
 | 
			
		||||
    assert_relations(tag.suggestions, ['tag1'])
 | 
			
		||||
    assert_relations(tag.implications, ['tag1'])
 | 
			
		||||
 | 
			
		||||
@pytest.mark.parametrize('input', [
 | 
			
		||||
    {
 | 
			
		||||
        'names': ['tag1'],
 | 
			
		||||
        'category': 'meta',
 | 
			
		||||
        'suggestions': ['tag1'],
 | 
			
		||||
        'implications': [],
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        'names': ['tag1'],
 | 
			
		||||
        'category': 'meta',
 | 
			
		||||
        'suggestions': [],
 | 
			
		||||
        'implications': ['tag1'],
 | 
			
		||||
    }
 | 
			
		||||
])
 | 
			
		||||
def test_trying_to_relate_tag_to_itself(test_ctx, input):
 | 
			
		||||
    db.session.add(test_ctx.tag_factory(names=['tag1']))
 | 
			
		||||
    db.session.commit()
 | 
			
		||||
    with pytest.raises(tags.InvalidTagRelationError):
 | 
			
		||||
        test_ctx.api.put(
 | 
			
		||||
            test_ctx.context_factory(
 | 
			
		||||
                input={**input, **{'version': 1}},
 | 
			
		||||
                user=test_ctx.user_factory(rank=db.User.RANK_REGULAR)),
 | 
			
		||||
            'tag1')
 | 
			
		||||
 | 
			
		||||
@pytest.mark.parametrize('input', [
 | 
			
		||||
    {'names': 'whatever'},
 | 
			
		||||
    {'category': 'whatever'},
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										478
									
								
								server/szurubooru/tests/func/test_tags.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										478
									
								
								server/szurubooru/tests/func/test_tags.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,478 @@
 | 
			
		||||
import os
 | 
			
		||||
import json
 | 
			
		||||
import pytest
 | 
			
		||||
import unittest.mock
 | 
			
		||||
from datetime import datetime
 | 
			
		||||
from szurubooru import db
 | 
			
		||||
from szurubooru.func import tags, tag_categories, cache, snapshots
 | 
			
		||||
 | 
			
		||||
@pytest.fixture(autouse=True)
 | 
			
		||||
def purge_cache(config_injector):
 | 
			
		||||
    cache.purge()
 | 
			
		||||
 | 
			
		||||
def _assert_tag_siblings(result, expected_tag_names_and_occurrences):
 | 
			
		||||
    actual_tag_names_and_occurences = []
 | 
			
		||||
    for sibling, occurrences in result:
 | 
			
		||||
        actual_tag_names_and_occurences.append((sibling.names[0].name, occurrences))
 | 
			
		||||
    assert actual_tag_names_and_occurences == expected_tag_names_and_occurrences
 | 
			
		||||
 | 
			
		||||
@pytest.mark.parametrize('input,expected_tag_names', [
 | 
			
		||||
    ([('a', 'a', True), ('b', 'b', False), ('c', 'c', False)], list('abc')),
 | 
			
		||||
    ([('c', 'a', True), ('b', 'b', False), ('a', 'c', False)], list('cba')),
 | 
			
		||||
    ([('a', 'c', True), ('b', 'b', False), ('c', 'a', False)], list('acb')),
 | 
			
		||||
    ([('a', 'c', False), ('b', 'b', False), ('c', 'a', True)], list('cba')),
 | 
			
		||||
])
 | 
			
		||||
def test_sort_tags(input, expected_tag_names, tag_factory, tag_category_factory):
 | 
			
		||||
    db_tags = []
 | 
			
		||||
    for tag in input:
 | 
			
		||||
        tag_name, category_name, category_is_default = tag
 | 
			
		||||
        db_tags.append(
 | 
			
		||||
            tag_factory(
 | 
			
		||||
                names=[tag_name],
 | 
			
		||||
                category=tag_category_factory(
 | 
			
		||||
                    name=category_name, default=category_is_default)))
 | 
			
		||||
    db.session.add_all(db_tags)
 | 
			
		||||
    actual_tag_names = [tag.names[0].name for tag in tags.sort_tags(db_tags)]
 | 
			
		||||
    assert actual_tag_names == expected_tag_names
 | 
			
		||||
 | 
			
		||||
def test_serialize_tag_when_empty():
 | 
			
		||||
    assert tags.serialize_tag(None, None) is None
 | 
			
		||||
 | 
			
		||||
def test_serialize_tag(post_factory, tag_factory, tag_category_factory):
 | 
			
		||||
    with unittest.mock.patch('szurubooru.func.snapshots.get_serialized_history'):
 | 
			
		||||
        snapshots.get_serialized_history.return_value = 'snapshot history'
 | 
			
		||||
 | 
			
		||||
        tag = tag_factory(
 | 
			
		||||
            names=['tag1', 'tag2'],
 | 
			
		||||
            category=tag_category_factory(name='cat'))
 | 
			
		||||
        tag.tag_id = 1
 | 
			
		||||
        tag.description = 'description'
 | 
			
		||||
        tag.suggestions = [tag_factory(names=['sug1']), tag_factory(names=['sug2'])]
 | 
			
		||||
        tag.implications = [tag_factory(names=['impl1']), tag_factory(names=['impl2'])]
 | 
			
		||||
        tag.last_edit_time = datetime(1998, 1, 1)
 | 
			
		||||
 | 
			
		||||
        post1 = post_factory()
 | 
			
		||||
        post2 = post_factory()
 | 
			
		||||
        post1.tags = [tag]
 | 
			
		||||
        post2.tags = [tag]
 | 
			
		||||
        db.session.add_all([tag, post1, post2])
 | 
			
		||||
        db.session.flush()
 | 
			
		||||
 | 
			
		||||
        result = tags.serialize_tag(tag)
 | 
			
		||||
        result['suggestions'].sort()
 | 
			
		||||
        result['implications'].sort()
 | 
			
		||||
 | 
			
		||||
    assert result ==  {
 | 
			
		||||
        'names': ['tag1', 'tag2'],
 | 
			
		||||
        'version': 1,
 | 
			
		||||
        'category': 'cat',
 | 
			
		||||
        'creationTime': datetime(1996, 1, 1, 0, 0),
 | 
			
		||||
        'lastEditTime': datetime(1998, 1, 1, 0, 0),
 | 
			
		||||
        'description': 'description',
 | 
			
		||||
        'suggestions': ['sug1', 'sug2'],
 | 
			
		||||
        'implications': ['impl1', 'impl2'],
 | 
			
		||||
        'usages': 2,
 | 
			
		||||
        'snapshots': 'snapshot history',
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
def test_export_to_json(
 | 
			
		||||
        tmpdir,
 | 
			
		||||
        query_counter,
 | 
			
		||||
        config_injector,
 | 
			
		||||
        post_factory,
 | 
			
		||||
        tag_factory,
 | 
			
		||||
        tag_category_factory):
 | 
			
		||||
    config_injector({'data_dir': str(tmpdir)})
 | 
			
		||||
    cat1 = tag_category_factory(name='cat1', color='black')
 | 
			
		||||
    cat2 = tag_category_factory(name='cat2', color='white')
 | 
			
		||||
    db.session.add_all([cat1, cat2])
 | 
			
		||||
    db.session.flush()
 | 
			
		||||
    sug1 = tag_factory(names=['sug1'], category=cat1)
 | 
			
		||||
    sug2 = tag_factory(names=['sug2'], category=cat1)
 | 
			
		||||
    imp1 = tag_factory(names=['imp1'], category=cat1)
 | 
			
		||||
    imp2 = tag_factory(names=['imp2'], category=cat1)
 | 
			
		||||
    tag = tag_factory(names=['alias1', 'alias2'], category=cat2)
 | 
			
		||||
    db.session.add_all([tag, sug1, sug2, imp1, imp2, cat1, cat2])
 | 
			
		||||
    post = post_factory()
 | 
			
		||||
    post.tags = [tag]
 | 
			
		||||
    db.session.flush()
 | 
			
		||||
    db.session.add_all([
 | 
			
		||||
        post,
 | 
			
		||||
        db.TagSuggestion(tag.tag_id, sug1.tag_id),
 | 
			
		||||
        db.TagSuggestion(tag.tag_id, sug2.tag_id),
 | 
			
		||||
        db.TagImplication(tag.tag_id, imp1.tag_id),
 | 
			
		||||
        db.TagImplication(tag.tag_id, imp2.tag_id),
 | 
			
		||||
    ])
 | 
			
		||||
    db.session.flush()
 | 
			
		||||
 | 
			
		||||
    with query_counter:
 | 
			
		||||
        tags.export_to_json()
 | 
			
		||||
        assert len(query_counter.statements) == 5
 | 
			
		||||
 | 
			
		||||
    export_path = os.path.join(str(tmpdir), 'tags.json')
 | 
			
		||||
    assert os.path.exists(export_path)
 | 
			
		||||
    with open(export_path, 'r') as handle:
 | 
			
		||||
        assert json.loads(handle.read()) == {
 | 
			
		||||
            'tags': [
 | 
			
		||||
                {
 | 
			
		||||
                    'names': ['alias1', 'alias2'],
 | 
			
		||||
                    'usages': 1,
 | 
			
		||||
                    'category': 'cat2',
 | 
			
		||||
                    'suggestions': ['sug1', 'sug2'],
 | 
			
		||||
                    'implications': ['imp1', 'imp2'],
 | 
			
		||||
                },
 | 
			
		||||
                {'names': ['sug1'], 'usages': 0, 'category': 'cat1'},
 | 
			
		||||
                {'names': ['sug2'], 'usages': 0, 'category': 'cat1'},
 | 
			
		||||
                {'names': ['imp1'], 'usages': 0, 'category': 'cat1'},
 | 
			
		||||
                {'names': ['imp2'], 'usages': 0, 'category': 'cat1'},
 | 
			
		||||
            ],
 | 
			
		||||
            'categories': [
 | 
			
		||||
                {'name': 'cat1', 'color': 'black'},
 | 
			
		||||
                {'name': 'cat2', 'color': 'white'},
 | 
			
		||||
            ]
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@pytest.mark.parametrize('name_to_search,expected_to_find', [
 | 
			
		||||
    ('name', True),
 | 
			
		||||
    ('NAME', True),
 | 
			
		||||
    ('alias', True),
 | 
			
		||||
    ('ALIAS', True),
 | 
			
		||||
    ('-', False),
 | 
			
		||||
])
 | 
			
		||||
def test_try_get_tag_by_name(name_to_search, expected_to_find, tag_factory):
 | 
			
		||||
    tag = tag_factory(names=['name', 'ALIAS'])
 | 
			
		||||
    db.session.add(tag)
 | 
			
		||||
    if expected_to_find:
 | 
			
		||||
        assert tags.try_get_tag_by_name(name_to_search) == tag
 | 
			
		||||
    else:
 | 
			
		||||
        assert tags.try_get_tag_by_name(name_to_search) is None
 | 
			
		||||
 | 
			
		||||
@pytest.mark.parametrize('name_to_search,expected_to_find', [
 | 
			
		||||
    ('name', True),
 | 
			
		||||
    ('NAME', True),
 | 
			
		||||
    ('alias', True),
 | 
			
		||||
    ('ALIAS', True),
 | 
			
		||||
    ('-', False),
 | 
			
		||||
])
 | 
			
		||||
def test_get_tag_by_name(name_to_search, expected_to_find, tag_factory):
 | 
			
		||||
    tag = tag_factory(names=['name', 'ALIAS'])
 | 
			
		||||
    db.session.add(tag)
 | 
			
		||||
    if expected_to_find:
 | 
			
		||||
        assert tags.get_tag_by_name(name_to_search) == tag
 | 
			
		||||
    else:
 | 
			
		||||
        with pytest.raises(tags.TagNotFoundError):
 | 
			
		||||
            tags.get_tag_by_name(name_to_search)
 | 
			
		||||
 | 
			
		||||
@pytest.mark.parametrize('names_to_search,expected_ids', [
 | 
			
		||||
    ([], []),
 | 
			
		||||
    (['name1'], [1]),
 | 
			
		||||
    (['NAME1'], [1]),
 | 
			
		||||
    (['alias1'], [1]),
 | 
			
		||||
    (['ALIAS1'], [1]),
 | 
			
		||||
    (['name2'], [2]),
 | 
			
		||||
    (['name1', 'name1'], [1]),
 | 
			
		||||
    (['name1', 'NAME1'], [1]),
 | 
			
		||||
    (['name1', 'alias1'], [1]),
 | 
			
		||||
    (['name1', 'alias2'], [1, 2]),
 | 
			
		||||
    (['NAME1', 'alias2'], [1, 2]),
 | 
			
		||||
    (['name1', 'ALIAS2'], [1, 2]),
 | 
			
		||||
    (['name2', 'alias1'], [1, 2]),
 | 
			
		||||
])
 | 
			
		||||
def test_get_tag_by_names(names_to_search, expected_ids, tag_factory):
 | 
			
		||||
    tag1 = tag_factory(names=['name1', 'ALIAS1'])
 | 
			
		||||
    tag2 = tag_factory(names=['name2', 'ALIAS2'])
 | 
			
		||||
    tag1.tag_id = 1
 | 
			
		||||
    tag2.tag_id = 2
 | 
			
		||||
    db.session.add_all([tag1, tag2])
 | 
			
		||||
    actual_ids = [tag.tag_id for tag in tags.get_tags_by_names(names_to_search)]
 | 
			
		||||
    assert actual_ids == expected_ids
 | 
			
		||||
 | 
			
		||||
@pytest.mark.parametrize(
 | 
			
		||||
    'names_to_search,expected_ids,expected_created_names', [
 | 
			
		||||
        ([], [], []),
 | 
			
		||||
        (['name1'], [1], []),
 | 
			
		||||
        (['NAME1'], [1], []),
 | 
			
		||||
        (['alias1'], [1], []),
 | 
			
		||||
        (['ALIAS1'], [1], []),
 | 
			
		||||
        (['name2'], [2], []),
 | 
			
		||||
        (['name1', 'name1'], [1], []),
 | 
			
		||||
        (['name1', 'NAME1'], [1], []),
 | 
			
		||||
        (['name1', 'alias1'], [1], []),
 | 
			
		||||
        (['name1', 'alias2'], [1, 2], []),
 | 
			
		||||
        (['NAME1', 'alias2'], [1, 2], []),
 | 
			
		||||
        (['name1', 'ALIAS2'], [1, 2], []),
 | 
			
		||||
        (['name2', 'alias1'], [1, 2], []),
 | 
			
		||||
        (['new'], [], ['new']),
 | 
			
		||||
        (['new', 'name1'], [1], ['new']),
 | 
			
		||||
        (['new', 'NAME1'], [1], ['new']),
 | 
			
		||||
        (['new', 'alias1'], [1], ['new']),
 | 
			
		||||
        (['new', 'ALIAS1'], [1], ['new']),
 | 
			
		||||
        (['new', 'name2'], [2], ['new']),
 | 
			
		||||
        (['new', 'name1', 'name1'], [1], ['new']),
 | 
			
		||||
        (['new', 'name1', 'NAME1'], [1], ['new']),
 | 
			
		||||
        (['new', 'name1', 'alias1'], [1], ['new']),
 | 
			
		||||
        (['new', 'name1', 'alias2'], [1, 2], ['new']),
 | 
			
		||||
        (['new', 'NAME1', 'alias2'], [1, 2], ['new']),
 | 
			
		||||
        (['new', 'name1', 'ALIAS2'], [1, 2], ['new']),
 | 
			
		||||
        (['new', 'name2', 'alias1'], [1, 2], ['new']),
 | 
			
		||||
        (['new', 'new'], [], ['new']),
 | 
			
		||||
        (['new', 'NEW'], [], ['new']),
 | 
			
		||||
        (['new', 'new2'], [], ['new', 'new2']),
 | 
			
		||||
    ])
 | 
			
		||||
def test_get_or_create_tags_by_names(
 | 
			
		||||
        names_to_search,
 | 
			
		||||
        expected_ids,
 | 
			
		||||
        expected_created_names,
 | 
			
		||||
        tag_factory,
 | 
			
		||||
        tag_category_factory,
 | 
			
		||||
        config_injector):
 | 
			
		||||
    config_injector({'tag_name_regex': '.*'})
 | 
			
		||||
    category = tag_category_factory()
 | 
			
		||||
    tag1 = tag_factory(names=['name1', 'ALIAS1'], category=category)
 | 
			
		||||
    tag2 = tag_factory(names=['name2', 'ALIAS2'], category=category)
 | 
			
		||||
    db.session.add_all([tag1, tag2])
 | 
			
		||||
    result = tags.get_or_create_tags_by_names(names_to_search)
 | 
			
		||||
    actual_ids = [tag.tag_id for tag in result[0]]
 | 
			
		||||
    actual_created_names = [tag.names[0].name for tag in result[1]]
 | 
			
		||||
    assert actual_ids == expected_ids
 | 
			
		||||
    assert actual_created_names == expected_created_names
 | 
			
		||||
 | 
			
		||||
def test_get_tag_siblings_for_unused(tag_factory, post_factory):
 | 
			
		||||
    tag = tag_factory(names=['tag'])
 | 
			
		||||
    db.session.add(tag)
 | 
			
		||||
    db.session.flush()
 | 
			
		||||
    _assert_tag_siblings(tags.get_tag_siblings(tag), [])
 | 
			
		||||
 | 
			
		||||
def test_get_tag_siblings_for_used_alone(tag_factory, post_factory):
 | 
			
		||||
    tag = tag_factory(names=['tag'])
 | 
			
		||||
    post = post_factory()
 | 
			
		||||
    post.tags = [tag]
 | 
			
		||||
    db.session.add_all([post, tag])
 | 
			
		||||
    db.session.flush()
 | 
			
		||||
    _assert_tag_siblings(tags.get_tag_siblings(tag), [])
 | 
			
		||||
 | 
			
		||||
def test_get_tag_siblings_for_used_with_others(tag_factory, post_factory):
 | 
			
		||||
    tag1 = tag_factory(names=['t1'])
 | 
			
		||||
    tag2 = tag_factory(names=['t2'])
 | 
			
		||||
    post = post_factory()
 | 
			
		||||
    post.tags = [tag1, tag2]
 | 
			
		||||
    db.session.add_all([post, tag1, tag2])
 | 
			
		||||
    db.session.flush()
 | 
			
		||||
    _assert_tag_siblings(tags.get_tag_siblings(tag1), [('t2', 1)])
 | 
			
		||||
    _assert_tag_siblings(tags.get_tag_siblings(tag2), [('t1', 1)])
 | 
			
		||||
 | 
			
		||||
def test_get_tag_siblings_used_for_multiple_others(tag_factory, post_factory):
 | 
			
		||||
    tag1 = tag_factory(names=['t1'])
 | 
			
		||||
    tag2 = tag_factory(names=['t2'])
 | 
			
		||||
    tag3 = tag_factory(names=['t3'])
 | 
			
		||||
    post1 = post_factory()
 | 
			
		||||
    post2 = post_factory()
 | 
			
		||||
    post3 = post_factory()
 | 
			
		||||
    post4 = post_factory()
 | 
			
		||||
    post1.tags = [tag1, tag2, tag3]
 | 
			
		||||
    post2.tags = [tag1, tag3]
 | 
			
		||||
    post3.tags = [tag2]
 | 
			
		||||
    post4.tags = [tag2]
 | 
			
		||||
    db.session.add_all([post1, post2, post3, post4, tag1, tag2, tag3])
 | 
			
		||||
    db.session.flush()
 | 
			
		||||
    _assert_tag_siblings(tags.get_tag_siblings(tag1), [('t3', 2), ('t2', 1)])
 | 
			
		||||
    _assert_tag_siblings(tags.get_tag_siblings(tag2), [('t1', 1), ('t3', 1)])
 | 
			
		||||
    # even though tag2 is used more widely, tag1 is more relevant to tag3
 | 
			
		||||
    _assert_tag_siblings(tags.get_tag_siblings(tag3), [('t1', 2), ('t2', 1)])
 | 
			
		||||
 | 
			
		||||
def test_delete(tag_factory):
 | 
			
		||||
    tag = tag_factory(names=['tag'])
 | 
			
		||||
    tag.suggestions = [tag_factory(names=['sug'])]
 | 
			
		||||
    tag.implications = [tag_factory(names=['imp'])]
 | 
			
		||||
    db.session.add(tag)
 | 
			
		||||
    db.session.flush()
 | 
			
		||||
    assert db.session.query(db.Tag).count() == 3
 | 
			
		||||
    tags.delete(tag)
 | 
			
		||||
    assert db.session.query(db.Tag).count() == 2
 | 
			
		||||
 | 
			
		||||
def test_merge_tags_without_usages(tag_factory):
 | 
			
		||||
    source_tag = tag_factory(names=['source'])
 | 
			
		||||
    target_tag = tag_factory(names=['target'])
 | 
			
		||||
    db.session.add_all([source_tag, target_tag])
 | 
			
		||||
    db.session.commit()
 | 
			
		||||
    tags.merge_tags(source_tag, target_tag)
 | 
			
		||||
    db.session.commit()
 | 
			
		||||
    assert tags.try_get_tag_by_name('source') is None
 | 
			
		||||
    tag = tags.get_tag_by_name('target')
 | 
			
		||||
    assert tag is not None
 | 
			
		||||
 | 
			
		||||
def test_merge_tags_with_usages(tag_factory, post_factory):
 | 
			
		||||
    source_tag = tag_factory(names=['source'])
 | 
			
		||||
    target_tag = tag_factory(names=['target'])
 | 
			
		||||
    post = post_factory()
 | 
			
		||||
    post.tags = [source_tag]
 | 
			
		||||
    db.session.add_all([source_tag, target_tag, post])
 | 
			
		||||
    db.session.commit()
 | 
			
		||||
    assert source_tag.post_count == 1
 | 
			
		||||
    assert target_tag.post_count == 0
 | 
			
		||||
    tags.merge_tags(source_tag, target_tag)
 | 
			
		||||
    db.session.commit()
 | 
			
		||||
    assert tags.try_get_tag_by_name('source') is None
 | 
			
		||||
    assert tags.get_tag_by_name('target').post_count == 1
 | 
			
		||||
 | 
			
		||||
def test_merge_tags_with_itself(tag_factory, post_factory):
 | 
			
		||||
    source_tag = tag_factory(names=['source'])
 | 
			
		||||
    db.session.add(source_tag)
 | 
			
		||||
    db.session.commit()
 | 
			
		||||
    with pytest.raises(tags.InvalidTagRelationError):
 | 
			
		||||
        tags.merge_tags(source_tag, source_tag)
 | 
			
		||||
 | 
			
		||||
def test_merge_tags_with_its_child_relation(tag_factory, post_factory):
 | 
			
		||||
    source_tag = tag_factory(names=['source'])
 | 
			
		||||
    target_tag = tag_factory(names=['target'])
 | 
			
		||||
    source_tag.suggestions = [target_tag]
 | 
			
		||||
    source_tag.implications = [target_tag]
 | 
			
		||||
    post = post_factory()
 | 
			
		||||
    post.tags = [source_tag, target_tag]
 | 
			
		||||
    db.session.add_all([source_tag, post])
 | 
			
		||||
    db.session.commit()
 | 
			
		||||
    tags.merge_tags(source_tag, target_tag)
 | 
			
		||||
    db.session.commit()
 | 
			
		||||
    assert tags.try_get_tag_by_name('source') is None
 | 
			
		||||
    assert tags.get_tag_by_name('target').post_count == 1
 | 
			
		||||
 | 
			
		||||
def test_merge_tags_with_its_parent_relation(tag_factory, post_factory):
 | 
			
		||||
    source_tag = tag_factory(names=['source'])
 | 
			
		||||
    target_tag = tag_factory(names=['target'])
 | 
			
		||||
    target_tag.suggestions = [source_tag]
 | 
			
		||||
    target_tag.implications = [source_tag]
 | 
			
		||||
    post = post_factory()
 | 
			
		||||
    post.tags = [source_tag, target_tag]
 | 
			
		||||
    db.session.add_all([source_tag, target_tag, post])
 | 
			
		||||
    db.session.commit()
 | 
			
		||||
    tags.merge_tags(source_tag, target_tag)
 | 
			
		||||
    db.session.commit()
 | 
			
		||||
    assert tags.try_get_tag_by_name('source') is None
 | 
			
		||||
    assert tags.get_tag_by_name('target').post_count == 1
 | 
			
		||||
 | 
			
		||||
def test_merge_tags_clears_relations(tag_factory):
 | 
			
		||||
    source_tag = tag_factory(names=['source'])
 | 
			
		||||
    target_tag = tag_factory(names=['target'])
 | 
			
		||||
    referring_tag = tag_factory(names=['parent'])
 | 
			
		||||
    referring_tag.suggestions = [source_tag]
 | 
			
		||||
    referring_tag.implications = [source_tag]
 | 
			
		||||
    db.session.add_all([source_tag, target_tag, referring_tag])
 | 
			
		||||
    db.session.commit()
 | 
			
		||||
    assert tags.try_get_tag_by_name('parent').implications != []
 | 
			
		||||
    assert tags.try_get_tag_by_name('parent').suggestions != []
 | 
			
		||||
    tags.merge_tags(source_tag, target_tag)
 | 
			
		||||
    db.session.commit()
 | 
			
		||||
    assert tags.try_get_tag_by_name('source') is None
 | 
			
		||||
    assert tags.try_get_tag_by_name('parent').implications == []
 | 
			
		||||
    assert tags.try_get_tag_by_name('parent').suggestions == []
 | 
			
		||||
 | 
			
		||||
def test_merge_tags_when_target_exists(tag_factory, post_factory):
 | 
			
		||||
    source_tag = tag_factory(names=['source'])
 | 
			
		||||
    target_tag = tag_factory(names=['target'])
 | 
			
		||||
    post = post_factory()
 | 
			
		||||
    post.tags = [source_tag, target_tag]
 | 
			
		||||
    db.session.add_all([source_tag, target_tag, post])
 | 
			
		||||
    db.session.commit()
 | 
			
		||||
    assert source_tag.post_count == 1
 | 
			
		||||
    assert target_tag.post_count == 1
 | 
			
		||||
    tags.merge_tags(source_tag, target_tag)
 | 
			
		||||
    db.session.commit()
 | 
			
		||||
    assert tags.try_get_tag_by_name('source') is None
 | 
			
		||||
    assert tags.get_tag_by_name('target').post_count == 1
 | 
			
		||||
 | 
			
		||||
def test_create_tag(fake_datetime):
 | 
			
		||||
    with unittest.mock.patch('szurubooru.func.tags.update_tag_names'), \
 | 
			
		||||
            unittest.mock.patch('szurubooru.func.tags.update_tag_category_name'), \
 | 
			
		||||
            unittest.mock.patch('szurubooru.func.tags.update_tag_suggestions'), \
 | 
			
		||||
            unittest.mock.patch('szurubooru.func.tags.update_tag_implications'), \
 | 
			
		||||
            fake_datetime('1997-01-01'):
 | 
			
		||||
        tag = tags.create_tag(['name'], 'cat', ['sug'], ['imp'])
 | 
			
		||||
        assert tag.creation_time == datetime(1997, 1, 1)
 | 
			
		||||
        assert tag.last_edit_time is None
 | 
			
		||||
        tags.update_tag_names.assert_called_once_with(tag, ['name'])
 | 
			
		||||
        tags.update_tag_category_name.assert_called_once_with(tag, 'cat')
 | 
			
		||||
        tags.update_tag_suggestions.assert_called_once_with(tag, ['sug'])
 | 
			
		||||
        tags.update_tag_implications.assert_called_once_with(tag, ['imp'])
 | 
			
		||||
 | 
			
		||||
def test_update_tag_category_name(tag_factory):
 | 
			
		||||
    with unittest.mock.patch('szurubooru.func.tag_categories.get_category_by_name'):
 | 
			
		||||
        tag_categories.get_category_by_name.return_value = 'returned category'
 | 
			
		||||
        tag = tag_factory()
 | 
			
		||||
        tags.update_tag_category_name(tag, 'cat')
 | 
			
		||||
        assert tag_categories.get_category_by_name.called_once_with('cat')
 | 
			
		||||
        assert tag.category == 'returned category'
 | 
			
		||||
 | 
			
		||||
def test_update_tag_names_to_empty(tag_factory):
 | 
			
		||||
    tag = tag_factory()
 | 
			
		||||
    with pytest.raises(tags.InvalidTagNameError):
 | 
			
		||||
        tags.update_tag_names(tag, [])
 | 
			
		||||
 | 
			
		||||
def test_update_tag_names_with_invalid_name(config_injector, tag_factory):
 | 
			
		||||
    config_injector({'tag_name_regex': '^[a-z]*$'})
 | 
			
		||||
    tag = tag_factory()
 | 
			
		||||
    with pytest.raises(tags.InvalidTagNameError):
 | 
			
		||||
        tags.update_tag_names(tag, ['0'])
 | 
			
		||||
 | 
			
		||||
def test_update_tag_names_with_too_long_string(config_injector, tag_factory):
 | 
			
		||||
    config_injector({'tag_name_regex': '^[a-z]*$'})
 | 
			
		||||
    tag = tag_factory()
 | 
			
		||||
    with pytest.raises(tags.InvalidTagNameError):
 | 
			
		||||
        tags.update_tag_names(tag, ['a' * 300])
 | 
			
		||||
 | 
			
		||||
def test_update_tag_names_with_duplicate_names(config_injector, tag_factory):
 | 
			
		||||
    config_injector({'tag_name_regex': '^[a-z]*$'})
 | 
			
		||||
    tag = tag_factory()
 | 
			
		||||
    tags.update_tag_names(tag, ['a', 'A'])
 | 
			
		||||
    assert [tag_name.name for tag_name in tag.names] == ['a']
 | 
			
		||||
 | 
			
		||||
def test_update_tag_names_trying_to_use_taken_name(config_injector, tag_factory):
 | 
			
		||||
    config_injector({'tag_name_regex': '^[a-zA-Z]*$'})
 | 
			
		||||
    existing_tag = tag_factory(names=['a'])
 | 
			
		||||
    db.session.add(existing_tag)
 | 
			
		||||
    tag = tag_factory()
 | 
			
		||||
    db.session.add(tag)
 | 
			
		||||
    with pytest.raises(tags.TagAlreadyExistsError):
 | 
			
		||||
        tags.update_tag_names(tag, ['a'])
 | 
			
		||||
    with pytest.raises(tags.TagAlreadyExistsError):
 | 
			
		||||
        tags.update_tag_names(tag, ['A'])
 | 
			
		||||
 | 
			
		||||
def test_update_tag_names_reusing_own_name(config_injector, tag_factory):
 | 
			
		||||
    config_injector({'tag_name_regex': '^[a-zA-Z]*$'})
 | 
			
		||||
    for name in list('aA'):
 | 
			
		||||
        tag = tag_factory(names=['a'])
 | 
			
		||||
        db.session.add(tag)
 | 
			
		||||
        db.session.flush()
 | 
			
		||||
        tags.update_tag_names(tag, [name])
 | 
			
		||||
        assert [tag_name.name for tag_name in tag.names] == [name]
 | 
			
		||||
        db.session.rollback()
 | 
			
		||||
 | 
			
		||||
@pytest.mark.parametrize('attempt', ['name', 'NAME', 'alias', 'ALIAS'])
 | 
			
		||||
def test_update_tag_suggestions_with_itself(attempt, tag_factory):
 | 
			
		||||
    tag = tag_factory(names=['name', 'ALIAS'])
 | 
			
		||||
    with pytest.raises(tags.InvalidTagRelationError):
 | 
			
		||||
        tags.update_tag_suggestions(tag, [attempt])
 | 
			
		||||
 | 
			
		||||
def test_update_tag_suggestions(tag_factory):
 | 
			
		||||
    tag = tag_factory(names=['name', 'ALIAS'])
 | 
			
		||||
    with unittest.mock.patch('szurubooru.func.tags.get_tags_by_names'):
 | 
			
		||||
        tags.get_tags_by_names.return_value = ['returned tags']
 | 
			
		||||
        tags.update_tag_suggestions(tag, ['test'])
 | 
			
		||||
        assert tag.suggestions == ['returned tags']
 | 
			
		||||
 | 
			
		||||
@pytest.mark.parametrize('attempt', ['name', 'NAME', 'alias', 'ALIAS'])
 | 
			
		||||
def test_update_tag_implications_with_itself(attempt, tag_factory):
 | 
			
		||||
    tag = tag_factory(names=['name', 'ALIAS'])
 | 
			
		||||
    with pytest.raises(tags.InvalidTagRelationError):
 | 
			
		||||
        tags.update_tag_implications(tag, [attempt])
 | 
			
		||||
 | 
			
		||||
def test_update_tag_implications(tag_factory):
 | 
			
		||||
    tag = tag_factory(names=['name', 'ALIAS'])
 | 
			
		||||
    with unittest.mock.patch('szurubooru.func.tags.get_tags_by_names'):
 | 
			
		||||
        tags.get_tags_by_names.return_value = ['returned tags']
 | 
			
		||||
        tags.update_tag_implications(tag, ['test'])
 | 
			
		||||
        assert tag.implications == ['returned tags']
 | 
			
		||||
 | 
			
		||||
def test_update_tag_description(tag_factory):
 | 
			
		||||
    tag = tag_factory()
 | 
			
		||||
    tags.update_tag_description(tag, 'test')
 | 
			
		||||
    assert tag.description == 'test'
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user