1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
|
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Resolvers::PaginatedTreeResolver, feature_category: :source_code_management do
include GraphqlHelpers
let_it_be(:project) { create(:project, :repository) }
let_it_be(:repository) { project.repository }
specify do
expect(described_class).to have_nullable_graphql_type(Types::Tree::TreeType.connection_type)
end
describe '#resolve', :aggregate_failures do
subject { resolve_repository(args, opts) }
let(:args) { { ref: 'master' } }
let(:opts) { {} }
let(:start_cursor) { subject.start_cursor }
let(:end_cursor) { subject.end_cursor }
let(:items) { subject.items }
let(:entries) { items.first.entries }
it 'resolves to a collection with a tree object' do
expect(items.first).to be_an_instance_of(Tree)
expect(start_cursor).to be_nil
expect(end_cursor).to be_blank
expect(entries.count).to eq(repository.tree.entries.count)
end
context 'with recursive option' do
let(:args) { super().merge(recursive: true) }
it 'resolve to a recursive tree' do
expect(entries[4].path).to eq('files/html')
end
end
context 'with limited max_page_size' do
let(:opts) { { max_page_size: 5 } }
it 'resolves to a pagination collection with a tree object' do
expect(items.first).to be_an_instance_of(Tree)
expect(start_cursor).to be_nil
expect(end_cursor).to be_present
expect(entries.count).to eq(5)
end
end
context 'when repository does not exist' do
before do
allow(repository).to receive(:exists?).and_return(false)
end
it 'returns nil' do
is_expected.to be(nil)
end
end
context 'when repository is empty' do
before do
allow(repository).to receive(:empty?).and_return(true)
end
it 'returns nil' do
is_expected.to be(nil)
end
end
describe 'Cursor pagination' do
context 'when cursor is invalid' do
let(:args) { super().merge(after: 'invalid') }
it 'generates an error' do
expect_graphql_error_to_be_created(Gitlab::Graphql::Errors::BaseError) { subject }
expect(subject.extensions.keys).to match_array([:code, :gitaly_code, :service])
end
end
it 'returns all tree entries during cursor pagination' do
cursor = nil
expected_entries = repository.tree.entries.map(&:path)
collected_entries = []
loop do
result = resolve_repository(args.merge(after: cursor), max_page_size: 10)
collected_entries += result.items.first.entries.map(&:path)
expect(result.start_cursor).to eq(cursor)
cursor = result.end_cursor
break if cursor.blank?
end
expect(collected_entries).to match_array(expected_entries)
end
end
describe 'Custom error handling' do
before do
grpc_err = GRPC::Unavailable.new
allow(repository).to receive(:tree).and_raise(Gitlab::Git::CommandError, grpc_err)
end
context 'when gitaly is not available' do
let(:request) { get :index, format: :html, params: { namespace_id: project.namespace, project_id: project } }
it 'generates an unavailable error' do
expect_graphql_error_to_be_created(Gitlab::Graphql::Errors::BaseError) { subject }
expect(subject.extensions).to eq(code: 'unavailable', gitaly_code: 14, service: 'git')
end
end
end
end
def resolve_repository(args, opts = {})
field_options = {
owner: resolver_parent,
resolver: described_class,
connection_extension: Gitlab::Graphql::Extensions::ExternallyPaginatedArrayExtension
}.merge(opts)
field = ::Types::BaseField.from_options('field_value', **field_options)
resolve_field(field, repository, args: args, object_type: resolver_parent)
end
end
|