Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
3b671ccd
Commit
3b671ccd
authored
Jan 18, 2022
by
George Koltsov
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add BulkImports Project Members Migration
parent
f8e21145
Changes
29
Hide whitespace changes
Inline
Side-by-side
Showing
29 changed files
with
517 additions
and
350 deletions
+517
-350
ee/lib/bulk_imports/groups/graphql/get_iterations_query.rb
ee/lib/bulk_imports/groups/graphql/get_iterations_query.rb
+7
-3
ee/spec/lib/bulk_imports/groups/graphql/get_iterations_query_spec.rb
.../bulk_imports/groups/graphql/get_iterations_query_spec.rb
+11
-9
ee/spec/lib/ee/bulk_imports/groups/stage_spec.rb
ee/spec/lib/ee/bulk_imports/groups/stage_spec.rb
+1
-1
ee/spec/lib/ee/bulk_imports/projects/stage_spec.rb
ee/spec/lib/ee/bulk_imports/projects/stage_spec.rb
+1
-0
lib/bulk_imports/common/extractors/graphql_extractor.rb
lib/bulk_imports/common/extractors/graphql_extractor.rb
+4
-3
lib/bulk_imports/common/graphql/get_members_query.rb
lib/bulk_imports/common/graphql/get_members_query.rb
+26
-11
lib/bulk_imports/common/pipelines/members_pipeline.rb
lib/bulk_imports/common/pipelines/members_pipeline.rb
+54
-0
lib/bulk_imports/groups/graphql/get_group_query.rb
lib/bulk_imports/groups/graphql/get_group_query.rb
+7
-3
lib/bulk_imports/groups/graphql/get_projects_query.rb
lib/bulk_imports/groups/graphql/get_projects_query.rb
+7
-3
lib/bulk_imports/groups/pipelines/members_pipeline.rb
lib/bulk_imports/groups/pipelines/members_pipeline.rb
+0
-26
lib/bulk_imports/groups/stage.rb
lib/bulk_imports/groups/stage.rb
+1
-1
lib/bulk_imports/groups/transformers/member_attributes_transformer.rb
...orts/groups/transformers/member_attributes_transformer.rb
+14
-29
lib/bulk_imports/projects/graphql/get_project_query.rb
lib/bulk_imports/projects/graphql/get_project_query.rb
+2
-3
lib/bulk_imports/projects/graphql/get_repository_query.rb
lib/bulk_imports/projects/graphql/get_repository_query.rb
+2
-3
lib/bulk_imports/projects/graphql/get_snippet_repository_query.rb
..._imports/projects/graphql/get_snippet_repository_query.rb
+3
-4
lib/bulk_imports/projects/graphql/queryable.rb
lib/bulk_imports/projects/graphql/queryable.rb
+7
-1
lib/bulk_imports/projects/stage.rb
lib/bulk_imports/projects/stage.rb
+4
-0
spec/lib/bulk_imports/common/extractors/graphql_extractor_spec.rb
.../bulk_imports/common/extractors/graphql_extractor_spec.rb
+9
-6
spec/lib/bulk_imports/common/graphql/get_members_query_spec.rb
...lib/bulk_imports/common/graphql/get_members_query_spec.rb
+56
-0
spec/lib/bulk_imports/common/pipelines/members_pipeline_spec.rb
...ib/bulk_imports/common/pipelines/members_pipeline_spec.rb
+161
-0
spec/lib/bulk_imports/groups/graphql/get_group_query_spec.rb
spec/lib/bulk_imports/groups/graphql/get_group_query_spec.rb
+20
-7
spec/lib/bulk_imports/groups/graphql/get_members_query_spec.rb
...lib/bulk_imports/groups/graphql/get_members_query_spec.rb
+0
-35
spec/lib/bulk_imports/groups/graphql/get_projects_query_spec.rb
...ib/bulk_imports/groups/graphql/get_projects_query_spec.rb
+20
-20
spec/lib/bulk_imports/groups/pipelines/members_pipeline_spec.rb
...ib/bulk_imports/groups/pipelines/members_pipeline_spec.rb
+0
-119
spec/lib/bulk_imports/groups/stage_spec.rb
spec/lib/bulk_imports/groups/stage_spec.rb
+1
-1
spec/lib/bulk_imports/groups/transformers/member_attributes_transformer_spec.rb
...groups/transformers/member_attributes_transformer_spec.rb
+12
-12
spec/lib/bulk_imports/projects/graphql/get_project_query_spec.rb
...b/bulk_imports/projects/graphql/get_project_query_spec.rb
+27
-0
spec/lib/bulk_imports/projects/graphql/get_repository_query_spec.rb
...ulk_imports/projects/graphql/get_repository_query_spec.rb
+21
-11
spec/lib/bulk_imports/projects/graphql/get_snippet_repository_query_spec.rb
...rts/projects/graphql/get_snippet_repository_query_spec.rb
+39
-39
No files found.
ee/lib/bulk_imports/groups/graphql/get_iterations_query.rb
View file @
3b671ccd
...
...
@@ -3,8 +3,12 @@
module
BulkImports
module
Groups
module
Graphql
module
GetIterationsQuery
extend
self
class
GetIterationsQuery
attr_reader
:context
def
initialize
(
context
:)
@context
=
context
end
def
to_s
<<-
'GRAPHQL'
...
...
@@ -31,7 +35,7 @@ module BulkImports
GRAPHQL
end
def
variables
(
context
)
def
variables
{
full_path:
context
.
entity
.
source_full_path
,
cursor:
context
.
tracker
.
next_page
,
...
...
ee/spec/lib/bulk_imports/groups/graphql/get_iterations_query_spec.rb
View file @
3b671ccd
...
...
@@ -3,16 +3,18 @@
require
'spec_helper'
RSpec
.
describe
BulkImports
::
Groups
::
Graphql
::
GetIterationsQuery
do
it
'has a valid query'
do
tracker
=
create
(
:bulk_import_tracker
)
context
=
BulkImports
::
Pipeline
::
Context
.
new
(
tracker
)
let_it_be
(
:tracker
)
{
create
(
:bulk_import_tracker
)
}
let_it_be
(
:context
)
{
BulkImports
::
Pipeline
::
Context
.
new
(
tracker
)
}
subject
(
:query
)
{
described_class
.
new
(
context:
context
)
}
query
=
GraphQL
::
Query
.
new
(
it
'has a valid query'
do
parsed_query
=
GraphQL
::
Query
.
new
(
GitlabSchema
,
described_class
.
to_s
,
variables:
described_class
.
variables
(
context
)
query
.
to_s
,
variables:
query
.
variables
)
result
=
GitlabSchema
.
static_validator
.
validate
(
query
)
result
=
GitlabSchema
.
static_validator
.
validate
(
parsed_
query
)
expect
(
result
[
:errors
]).
to
be_empty
end
...
...
@@ -21,7 +23,7 @@ RSpec.describe BulkImports::Groups::Graphql::GetIterationsQuery do
it
'returns data path'
do
expected
=
%w[data group iterations nodes]
expect
(
described_class
.
data_path
).
to
eq
(
expected
)
expect
(
query
.
data_path
).
to
eq
(
expected
)
end
end
...
...
@@ -29,7 +31,7 @@ RSpec.describe BulkImports::Groups::Graphql::GetIterationsQuery do
it
'returns pagination information path'
do
expected
=
%w[data group iterations page_info]
expect
(
described_class
.
page_info_path
).
to
eq
(
expected
)
expect
(
query
.
page_info_path
).
to
eq
(
expected
)
end
end
end
ee/spec/lib/ee/bulk_imports/groups/stage_spec.rb
View file @
3b671ccd
...
...
@@ -7,7 +7,7 @@ RSpec.describe BulkImports::Groups::Stage do
[
[
0
,
BulkImports
::
Groups
::
Pipelines
::
GroupPipeline
],
[
1
,
BulkImports
::
Groups
::
Pipelines
::
SubgroupEntitiesPipeline
],
[
1
,
BulkImports
::
Groups
::
Pipelines
::
MembersPipeline
],
[
1
,
BulkImports
::
Common
::
Pipelines
::
MembersPipeline
],
[
1
,
BulkImports
::
Common
::
Pipelines
::
LabelsPipeline
],
[
1
,
BulkImports
::
Common
::
Pipelines
::
MilestonesPipeline
],
[
1
,
BulkImports
::
Common
::
Pipelines
::
BadgesPipeline
],
...
...
ee/spec/lib/ee/bulk_imports/projects/stage_spec.rb
View file @
3b671ccd
...
...
@@ -8,6 +8,7 @@ RSpec.describe BulkImports::Projects::Stage do
[
0
,
BulkImports
::
Projects
::
Pipelines
::
ProjectPipeline
],
[
1
,
BulkImports
::
Projects
::
Pipelines
::
RepositoryPipeline
],
[
1
,
BulkImports
::
Projects
::
Pipelines
::
ProjectAttributesPipeline
],
[
1
,
BulkImports
::
Common
::
Pipelines
::
MembersPipeline
],
[
2
,
BulkImports
::
Common
::
Pipelines
::
LabelsPipeline
],
[
2
,
BulkImports
::
Common
::
Pipelines
::
MilestonesPipeline
],
[
2
,
BulkImports
::
Common
::
Pipelines
::
BadgesPipeline
],
...
...
lib/bulk_imports/common/extractors/graphql_extractor.rb
View file @
3b671ccd
...
...
@@ -5,15 +5,16 @@ module BulkImports
module
Extractors
class
GraphqlExtractor
def
initialize
(
options
=
{})
@query
=
options
[
:query
]
@query
_klass
=
options
[
:query
]
end
def
extract
(
context
)
client
=
graphql_client
(
context
)
query
=
query_klass
.
new
(
context:
context
)
response
=
client
.
execute
(
client
.
parse
(
query
.
to_s
),
query
.
variables
(
context
)
query
.
variables
).
original_hash
.
deep_dup
BulkImports
::
Pipeline
::
ExtractedData
.
new
(
...
...
@@ -24,7 +25,7 @@ module BulkImports
private
attr_reader
:query
attr_reader
:query
_klass
def
graphql_client
(
context
)
@graphql_client
||=
BulkImports
::
Clients
::
Graphql
.
new
(
...
...
lib/bulk_imports/
groups
/graphql/get_members_query.rb
→
lib/bulk_imports/
common
/graphql/get_members_query.rb
View file @
3b671ccd
# frozen_string_literal: true
module
BulkImports
module
Groups
module
Common
module
Graphql
module
GetMembersQuery
extend
self
class
GetMembersQuery
attr_reader
:context
def
initialize
(
context
:)
@context
=
context
end
def
to_s
<<-
'GRAPHQL'
<<-
GRAPHQL
query($full_path: ID!, $cursor: String, $per_page: Int) {
group
(fullPath: $full_path) {
group_members: groupMembers(relations: DIRECT
, first: $per_page, after: $cursor) {
portable:
#{
context
.
entity
.
entity_type
}
(fullPath: $full_path) {
members:
#{
members_type
}
(relations: [DIRECT, INHERITED]
, first: $per_page, after: $cursor) {
page_info: pageInfo {
next_page: endCursor
has_next_page: hasNextPage
...
...
@@ -32,7 +37,7 @@ module BulkImports
GRAPHQL
end
def
variables
(
context
)
def
variables
{
full_path:
context
.
entity
.
source_full_path
,
cursor:
context
.
tracker
.
next_page
,
...
...
@@ -40,10 +45,6 @@ module BulkImports
}
end
def
base_path
%w[data group group_members]
end
def
data_path
base_path
<<
'nodes'
end
...
...
@@ -51,6 +52,20 @@ module BulkImports
def
page_info_path
base_path
<<
'page_info'
end
private
def
base_path
%w[data portable members]
end
def
members_type
if
context
.
entity
.
group?
'groupMembers'
else
'projectMembers'
end
end
end
end
end
...
...
lib/bulk_imports/common/pipelines/members_pipeline.rb
0 → 100644
View file @
3b671ccd
# frozen_string_literal: true
module
BulkImports
module
Common
module
Pipelines
class
MembersPipeline
include
Pipeline
transformer
Common
::
Transformers
::
ProhibitedAttributesTransformer
transformer
BulkImports
::
Groups
::
Transformers
::
MemberAttributesTransformer
def
extract
(
context
)
graphql_extractor
.
extract
(
context
)
end
def
load
(
_context
,
data
)
return
unless
data
user_id
=
data
[
:user_id
]
# Current user is already a member
return
if
user_id
==
current_user
.
id
user_membership
=
existing_user_membership
(
user_id
)
# User is already a member with higher existing (inherited) membership
return
if
user_membership
&&
user_membership
[
:access_level
]
>=
data
[
:access_level
]
# Create new membership for any other access level
portable
.
members
.
create!
(
data
)
end
private
def
graphql_extractor
@graphql_extractor
||=
BulkImports
::
Common
::
Extractors
::
GraphqlExtractor
.
new
(
query:
BulkImports
::
Common
::
Graphql
::
GetMembersQuery
)
end
def
existing_user_membership
(
user_id
)
members_finder
.
execute
.
find_by_user_id
(
user_id
)
end
def
members_finder
@members_finder
||=
if
context
.
entity
.
group?
::
GroupMembersFinder
.
new
(
portable
,
current_user
)
else
::
MembersFinder
.
new
(
portable
,
current_user
)
end
end
end
end
end
end
lib/bulk_imports/groups/graphql/get_group_query.rb
View file @
3b671ccd
...
...
@@ -3,8 +3,12 @@
module
BulkImports
module
Groups
module
Graphql
module
GetGroupQuery
extend
self
class
GetGroupQuery
attr_reader
:context
def
initialize
(
context
:)
@context
=
context
end
def
to_s
<<-
'GRAPHQL'
...
...
@@ -29,7 +33,7 @@ module BulkImports
GRAPHQL
end
def
variables
(
context
)
def
variables
{
full_path:
context
.
entity
.
source_full_path
}
end
...
...
lib/bulk_imports/groups/graphql/get_projects_query.rb
View file @
3b671ccd
...
...
@@ -3,8 +3,12 @@
module
BulkImports
module
Groups
module
Graphql
module
GetProjectsQuery
extend
self
class
GetProjectsQuery
attr_reader
:context
def
initialize
(
context
:)
@context
=
context
end
def
to_s
<<-
'GRAPHQL'
...
...
@@ -25,7 +29,7 @@ module BulkImports
GRAPHQL
end
def
variables
(
context
)
def
variables
{
full_path:
context
.
entity
.
source_full_path
,
cursor:
context
.
tracker
.
next_page
,
...
...
lib/bulk_imports/groups/pipelines/members_pipeline.rb
deleted
100644 → 0
View file @
f8e21145
# frozen_string_literal: true
module
BulkImports
module
Groups
module
Pipelines
class
MembersPipeline
include
Pipeline
extractor
BulkImports
::
Common
::
Extractors
::
GraphqlExtractor
,
query:
BulkImports
::
Groups
::
Graphql
::
GetMembersQuery
transformer
Common
::
Transformers
::
ProhibitedAttributesTransformer
transformer
BulkImports
::
Groups
::
Transformers
::
MemberAttributesTransformer
def
load
(
context
,
data
)
return
unless
data
# Current user is already a member
return
if
data
[
'user_id'
].
to_i
==
context
.
current_user
.
id
context
.
group
.
members
.
create!
(
data
)
end
end
end
end
end
lib/bulk_imports/groups/stage.rb
View file @
3b671ccd
...
...
@@ -16,7 +16,7 @@ module BulkImports
stage:
1
},
members:
{
pipeline:
BulkImports
::
Groups
::
Pipelines
::
MembersPipeline
,
pipeline:
BulkImports
::
Common
::
Pipelines
::
MembersPipeline
,
stage:
1
},
labels:
{
...
...
lib/bulk_imports/groups/transformers/member_attributes_transformer.rb
View file @
3b671ccd
...
...
@@ -5,50 +5,35 @@ module BulkImports
module
Transformers
class
MemberAttributesTransformer
def
transform
(
context
,
data
)
data
.
then
{
|
data
|
add_user
(
data
,
context
)
}
.
then
{
|
data
|
add_access_level
(
data
)
}
.
then
{
|
data
|
add_author
(
data
,
context
)
}
end
private
def
add_user
(
data
,
context
)
user
=
find_user
(
data
&
.
dig
(
'user'
,
'public_email'
))
access_level
=
data
&
.
dig
(
'access_level'
,
'integer_value'
)
return
unless
data
return
unless
user
return
unless
valid_access_level?
(
access_level
)
cache_source_user_id
(
data
,
user
,
context
)
data
.
except
(
'user'
)
.
merge
(
'user_id'
=>
user
.
id
)
{
user_id:
user
.
id
,
access_level:
access_level
,
created_at:
data
[
'created_at'
],
updated_at:
data
[
'updated_at'
],
expires_at:
data
[
'expires_at'
],
created_by_id:
context
.
current_user
.
id
}
end
private
def
find_user
(
email
)
return
unless
email
User
.
find_by_any_email
(
email
,
confirmed:
true
)
end
def
add_access_level
(
data
)
access_level
=
data
&
.
dig
(
'access_level'
,
'integer_value'
)
return
unless
valid_access_level?
(
access_level
)
data
.
merge
(
'access_level'
=>
access_level
)
end
def
valid_access_level?
(
access_level
)
Gitlab
::
Access
.
options_with_owner
.
value?
(
access_level
)
end
def
add_author
(
data
,
context
)
return
unless
data
data
.
merge
(
'created_by_id'
=>
context
.
current_user
.
id
)
Gitlab
::
Access
.
options_with_owner
.
value?
(
access_level
)
end
def
cache_source_user_id
(
data
,
user
,
context
)
...
...
lib/bulk_imports/projects/graphql/get_project_query.rb
View file @
3b671ccd
...
...
@@ -3,9 +3,8 @@
module
BulkImports
module
Projects
module
Graphql
module
GetProjectQuery
extend
Queryable
extend
self
class
GetProjectQuery
include
Queryable
def
to_s
<<-
'GRAPHQL'
...
...
lib/bulk_imports/projects/graphql/get_repository_query.rb
View file @
3b671ccd
...
...
@@ -3,9 +3,8 @@
module
BulkImports
module
Projects
module
Graphql
module
GetRepositoryQuery
extend
Queryable
extend
self
class
GetRepositoryQuery
include
Queryable
def
to_s
<<-
'GRAPHQL'
...
...
lib/bulk_imports/projects/graphql/get_snippet_repository_query.rb
View file @
3b671ccd
...
...
@@ -3,9 +3,8 @@
module
BulkImports
module
Projects
module
Graphql
module
GetSnippetRepositoryQuery
extend
Queryable
extend
self
class
GetSnippetRepositoryQuery
include
Queryable
def
to_s
<<-
'GRAPHQL'
...
...
@@ -27,7 +26,7 @@ module BulkImports
GRAPHQL
end
def
variables
(
context
)
def
variables
{
full_path:
context
.
entity
.
source_full_path
,
cursor:
context
.
tracker
.
next_page
,
...
...
lib/bulk_imports/projects/graphql/queryable.rb
View file @
3b671ccd
...
...
@@ -4,7 +4,13 @@ module BulkImports
module
Projects
module
Graphql
module
Queryable
def
variables
(
context
)
attr_reader
:context
def
initialize
(
context
:)
@context
=
context
end
def
variables
{
full_path:
context
.
entity
.
source_full_path
}
end
...
...
lib/bulk_imports/projects/stage.rb
View file @
3b671ccd
...
...
@@ -19,6 +19,10 @@ module BulkImports
pipeline:
BulkImports
::
Projects
::
Pipelines
::
ProjectAttributesPipeline
,
stage:
1
},
members:
{
pipeline:
BulkImports
::
Common
::
Pipelines
::
MembersPipeline
,
stage:
1
},
labels:
{
pipeline:
BulkImports
::
Common
::
Pipelines
::
LabelsPipeline
,
stage:
2
...
...
spec/lib/bulk_imports/common/extractors/graphql_extractor_spec.rb
View file @
3b671ccd
...
...
@@ -8,12 +8,15 @@ RSpec.describe BulkImports::Common::Extractors::GraphqlExtractor do
let
(
:response
)
{
double
(
original_hash:
{
'data'
=>
{
'foo'
=>
'bar'
},
'page_info'
=>
{}
})
}
let
(
:options
)
do
{
query:
double
(
to_s:
'test'
,
variables:
{},
data_path:
%w[data foo]
,
page_info_path:
%w[data page_info]
)
query:
double
(
new:
double
(
to_s:
'test'
,
variables:
{},
data_path:
%w[data foo]
,
page_info_path:
%w[data page_info]
)
)
}
end
...
...
spec/lib/bulk_imports/common/graphql/get_members_query_spec.rb
0 → 100644
View file @
3b671ccd
# frozen_string_literal: true
require
'spec_helper'
RSpec
.
describe
BulkImports
::
Common
::
Graphql
::
GetMembersQuery
do
let
(
:entity
)
{
create
(
:bulk_import_entity
,
:group_entity
)
}
let
(
:tracker
)
{
create
(
:bulk_import_tracker
,
entity:
entity
)
}
let
(
:context
)
{
BulkImports
::
Pipeline
::
Context
.
new
(
tracker
)
}
subject
(
:query
)
{
described_class
.
new
(
context:
context
)
}
it
'has a valid query'
do
parsed_query
=
GraphQL
::
Query
.
new
(
GitlabSchema
,
query
.
to_s
,
variables:
query
.
variables
)
result
=
GitlabSchema
.
static_validator
.
validate
(
parsed_query
)
expect
(
result
[
:errors
]).
to
be_empty
end
describe
'#data_path'
do
it
'returns data path'
do
expected
=
%w[data portable members nodes]
expect
(
query
.
data_path
).
to
eq
(
expected
)
end
end
describe
'#page_info_path'
do
it
'returns pagination information path'
do
expected
=
%w[data portable members page_info]
expect
(
query
.
page_info_path
).
to
eq
(
expected
)
end
end
describe
'#to_s'
do
context
'when entity is group'
do
it
'queries group & group members'
do
expect
(
query
.
to_s
).
to
include
(
'group'
)
expect
(
query
.
to_s
).
to
include
(
'groupMembers'
)
end
end
context
'when entity is project'
do
let
(
:entity
)
{
create
(
:bulk_import_entity
,
:project_entity
)
}
it
'queries project & project members'
do
expect
(
query
.
to_s
).
to
include
(
'project'
)
expect
(
query
.
to_s
).
to
include
(
'projectMembers'
)
end
end
end
end
spec/lib/bulk_imports/common/pipelines/members_pipeline_spec.rb
0 → 100644
View file @
3b671ccd
# frozen_string_literal: true
require
'spec_helper'
RSpec
.
describe
BulkImports
::
Common
::
Pipelines
::
MembersPipeline
do
let_it_be
(
:user
)
{
create
(
:user
)
}
let_it_be
(
:bulk_import
)
{
create
(
:bulk_import
,
user:
user
)
}
let_it_be
(
:member_user1
)
{
create
(
:user
,
email:
'email1@email.com'
)
}
let_it_be
(
:member_user2
)
{
create
(
:user
,
email:
'email2@email.com'
)
}
let_it_be
(
:member_data
)
do
{
user_id:
member_user1
.
id
,
created_by_id:
member_user2
.
id
,
access_level:
30
,
created_at:
'2020-01-01T00:00:00Z'
,
updated_at:
'2020-01-01T00:00:00Z'
,
expires_at:
nil
}
end
let
(
:parent
)
{
create
(
:group
)
}
let
(
:tracker
)
{
create
(
:bulk_import_tracker
,
entity:
entity
)
}
let
(
:context
)
{
BulkImports
::
Pipeline
::
Context
.
new
(
tracker
)
}
let
(
:members
)
{
portable
.
members
.
map
{
|
m
|
m
.
slice
(
:user_id
,
:access_level
)
}
}
subject
(
:pipeline
)
{
described_class
.
new
(
context
)
}
def
extracted_data
(
email
:,
has_next_page:
false
)
data
=
{
'created_at'
=>
'2020-01-01T00:00:00Z'
,
'updated_at'
=>
'2020-01-02T00:00:00Z'
,
'expires_at'
=>
nil
,
'access_level'
=>
{
'integer_value'
=>
30
},
'user'
=>
{
'public_email'
=>
email
}
}
page_info
=
{
'has_next_page'
=>
has_next_page
,
'next_page'
=>
has_next_page
?
'cursor'
:
nil
}
BulkImports
::
Pipeline
::
ExtractedData
.
new
(
data:
data
,
page_info:
page_info
)
end
shared_examples
'members import'
do
before
do
portable
.
members
.
delete_all
end
describe
'#run'
do
it
'creates memberships for existing users'
do
first_page
=
extracted_data
(
email:
member_user1
.
email
,
has_next_page:
true
)
last_page
=
extracted_data
(
email:
member_user2
.
email
)
allow_next_instance_of
(
BulkImports
::
Common
::
Extractors
::
GraphqlExtractor
)
do
|
extractor
|
allow
(
extractor
).
to
receive
(
:extract
).
and_return
(
first_page
,
last_page
)
end
expect
{
pipeline
.
run
}.
to
change
(
portable
.
members
,
:count
).
by
(
2
)
expect
(
members
).
to
contain_exactly
(
{
user_id:
member_user1
.
id
,
access_level:
30
},
{
user_id:
member_user2
.
id
,
access_level:
30
}
)
end
end
describe
'#load'
do
it
'creates new membership'
do
expect
{
subject
.
load
(
context
,
member_data
)
}.
to
change
(
portable
.
members
,
:count
).
by
(
1
)
member
=
portable
.
members
.
find_by_user_id
(
member_user1
.
id
)
expect
(
member
.
user
).
to
eq
(
member_user1
)
expect
(
member
.
created_by
).
to
eq
(
member_user2
)
expect
(
member
.
access_level
).
to
eq
(
30
)
expect
(
member
.
created_at
).
to
eq
(
'2020-01-01T00:00:00Z'
)
expect
(
member
.
updated_at
).
to
eq
(
'2020-01-01T00:00:00Z'
)
expect
(
member
.
expires_at
).
to
eq
(
nil
)
end
context
'when user_id is current user id'
do
it
'does not create new membership'
do
data
=
{
user_id:
user
.
id
}
expect
{
pipeline
.
load
(
context
,
data
)
}.
not_to
change
(
portable
.
members
,
:count
)
end
end
context
'when data is nil'
do
it
'does not create new membership'
do
expect
{
pipeline
.
load
(
context
,
nil
)
}.
not_to
change
(
portable
.
members
,
:count
)
end
end
context
'when user membership already exists with the same access level'
do
it
'does not create new membership'
do
portable
.
members
.
create!
(
member_data
)
expect
{
pipeline
.
load
(
context
,
member_data
)
}.
not_to
change
(
portable
.
members
,
:count
)
end
end
context
'when portable is in a parent group'
do
let
(
:tracker
)
{
create
(
:bulk_import_tracker
,
entity:
entity_with_parent
)
}
before
do
parent
.
members
.
create!
(
member_data
)
end
context
'when the same membership exists in parent group'
do
it
'does not create new membership'
do
expect
{
pipeline
.
load
(
context
,
member_data
)
}.
not_to
change
(
portable_with_parent
.
members
,
:count
)
end
end
context
'when membership with higher access level exists in parent group'
do
it
'creates new direct membership'
do
data
=
member_data
.
merge
(
access_level:
Gitlab
::
Access
::
MAINTAINER
)
expect
{
pipeline
.
load
(
context
,
data
)
}.
to
change
(
portable_with_parent
.
members
,
:count
)
member
=
portable_with_parent
.
members
.
find_by_user_id
(
member_user1
.
id
)
expect
(
member
.
access_level
).
to
eq
(
Gitlab
::
Access
::
MAINTAINER
)
end
end
context
'when membership with lower access level exists in parent group'
do
it
'does not create new membership'
do
data
=
member_data
.
merge
(
access_level:
Gitlab
::
Access
::
GUEST
)
expect
{
pipeline
.
load
(
context
,
data
)
}.
not_to
change
(
portable_with_parent
.
members
,
:count
)
end
end
end
end
end
context
'when importing to group'
do
let
(
:portable
)
{
create
(
:group
)
}
let
(
:portable_with_parent
)
{
create
(
:group
,
parent:
parent
)
}
let
(
:entity
)
{
create
(
:bulk_import_entity
,
:group_entity
,
group:
portable
,
bulk_import:
bulk_import
)
}
let
(
:entity_with_parent
)
{
create
(
:bulk_import_entity
,
:group_entity
,
group:
portable_with_parent
,
bulk_import:
bulk_import
)
}
include_examples
'members import'
end
context
'when importing to project'
do
let
(
:portable
)
{
create
(
:project
)
}
let
(
:portable_with_parent
)
{
create
(
:project
,
namespace:
parent
)
}
let
(
:entity
)
{
create
(
:bulk_import_entity
,
:project_entity
,
project:
portable
,
bulk_import:
bulk_import
)
}
let
(
:entity_with_parent
)
{
create
(
:bulk_import_entity
,
:project_entity
,
project:
portable_with_parent
,
bulk_import:
bulk_import
)
}
include_examples
'members import'
end
end
spec/lib/bulk_imports/groups/graphql/get_group_query_spec.rb
View file @
3b671ccd
...
...
@@ -3,14 +3,27 @@
require
'spec_helper'
RSpec
.
describe
BulkImports
::
Groups
::
Graphql
::
GetGroupQuery
do
let_it_be
(
:tracker
)
{
create
(
:bulk_import_tracker
)
}
let_it_be
(
:context
)
{
BulkImports
::
Pipeline
::
Context
.
new
(
tracker
)
}
subject
(
:query
)
{
described_class
.
new
(
context:
context
)
}
it
'has a valid query'
do
parsed_query
=
GraphQL
::
Query
.
new
(
GitlabSchema
,
query
.
to_s
,
variables:
query
.
variables
)
result
=
GitlabSchema
.
static_validator
.
validate
(
parsed_query
)
expect
(
result
[
:errors
]).
to
be_empty
end
describe
'#variables'
do
it
'returns query variables based on entity information'
do
entity
=
double
(
source_full_path:
'test'
,
bulk_import:
nil
)
tracker
=
double
(
entity:
entity
)
context
=
BulkImports
::
Pipeline
::
Context
.
new
(
tracker
)
expected
=
{
full_path:
entity
.
source_full_path
}
expected
=
{
full_path:
tracker
.
entity
.
source_full_path
}
expect
(
described_class
.
variables
(
context
)
).
to
eq
(
expected
)
expect
(
subject
.
variables
).
to
eq
(
expected
)
end
end
...
...
@@ -18,7 +31,7 @@ RSpec.describe BulkImports::Groups::Graphql::GetGroupQuery do
it
'returns data path'
do
expected
=
%w[data group]
expect
(
described_class
.
data_path
).
to
eq
(
expected
)
expect
(
subject
.
data_path
).
to
eq
(
expected
)
end
end
...
...
@@ -26,7 +39,7 @@ RSpec.describe BulkImports::Groups::Graphql::GetGroupQuery do
it
'returns pagination information path'
do
expected
=
%w[data group page_info]
expect
(
described_class
.
page_info_path
).
to
eq
(
expected
)
expect
(
subject
.
page_info_path
).
to
eq
(
expected
)
end
end
end
spec/lib/bulk_imports/groups/graphql/get_members_query_spec.rb
deleted
100644 → 0
View file @
f8e21145
# frozen_string_literal: true
require
'spec_helper'
RSpec
.
describe
BulkImports
::
Groups
::
Graphql
::
GetMembersQuery
do
it
'has a valid query'
do
tracker
=
create
(
:bulk_import_tracker
)
context
=
BulkImports
::
Pipeline
::
Context
.
new
(
tracker
)
query
=
GraphQL
::
Query
.
new
(
GitlabSchema
,
described_class
.
to_s
,
variables:
described_class
.
variables
(
context
)
)
result
=
GitlabSchema
.
static_validator
.
validate
(
query
)
expect
(
result
[
:errors
]).
to
be_empty
end
describe
'#data_path'
do
it
'returns data path'
do
expected
=
%w[data group group_members nodes]
expect
(
described_class
.
data_path
).
to
eq
(
expected
)
end
end
describe
'#page_info_path'
do
it
'returns pagination information path'
do
expected
=
%w[data group group_members page_info]
expect
(
described_class
.
page_info_path
).
to
eq
(
expected
)
end
end
end
spec/lib/bulk_imports/groups/graphql/get_projects_query_spec.rb
View file @
3b671ccd
...
...
@@ -3,25 +3,25 @@
require
'spec_helper'
RSpec
.
describe
BulkImports
::
Groups
::
Graphql
::
GetProjectsQuery
do
describe
'#variables'
do
it
'returns valid variables based on entity information'
do
tracker
=
create
(
:bulk_import_tracker
)
context
=
BulkImports
::
Pipeline
::
Context
.
new
(
tracker
)
query
=
GraphQL
::
Query
.
new
(
GitlabSchema
,
described_class
.
to_s
,
variables:
described_class
.
variables
(
context
)
)
result
=
GitlabSchema
.
static_validator
.
validate
(
query
)
expect
(
result
[
:errors
]).
to
be_empty
end
let_it_be
(
:tracker
)
{
create
(
:bulk_import_tracker
)
}
let_it_be
(
:context
)
{
BulkImports
::
Pipeline
::
Context
.
new
(
tracker
)
}
subject
(
:query
)
{
described_class
.
new
(
context:
context
)
}
it
'has a valid query'
do
parsed_query
=
GraphQL
::
Query
.
new
(
GitlabSchema
,
query
.
to_s
,
variables:
query
.
variables
)
result
=
GitlabSchema
.
static_validator
.
validate
(
parsed_query
)
expect
(
result
[
:errors
]).
to
be_empty
end
context
'with invalid variables'
do
it
'raises an error'
do
expect
{
GraphQL
::
Query
.
new
(
GitlabSchema
,
described_class
.
to_s
,
variables:
'invalid'
)
}.
to
raise_error
(
ArgumentError
)
end
context
'with invalid variables'
do
it
'raises an error'
do
expect
{
GraphQL
::
Query
.
new
(
GitlabSchema
,
subject
.
to_s
,
variables:
'invalid'
)
}.
to
raise_error
(
ArgumentError
)
end
end
...
...
@@ -29,7 +29,7 @@ RSpec.describe BulkImports::Groups::Graphql::GetProjectsQuery do
it
'returns data path'
do
expected
=
%w[data group projects nodes]
expect
(
described_class
.
data_path
).
to
eq
(
expected
)
expect
(
subject
.
data_path
).
to
eq
(
expected
)
end
end
...
...
@@ -37,7 +37,7 @@ RSpec.describe BulkImports::Groups::Graphql::GetProjectsQuery do
it
'returns pagination information path'
do
expected
=
%w[data group projects page_info]
expect
(
described_class
.
page_info_path
).
to
eq
(
expected
)
expect
(
subject
.
page_info_path
).
to
eq
(
expected
)
end
end
end
spec/lib/bulk_imports/groups/pipelines/members_pipeline_spec.rb
deleted
100644 → 0
View file @
f8e21145
# frozen_string_literal: true
require
'spec_helper'
RSpec
.
describe
BulkImports
::
Groups
::
Pipelines
::
MembersPipeline
do
let_it_be
(
:member_user1
)
{
create
(
:user
,
email:
'email1@email.com'
)
}
let_it_be
(
:member_user2
)
{
create
(
:user
,
email:
'email2@email.com'
)
}
let_it_be
(
:user
)
{
create
(
:user
)
}
let_it_be
(
:group
)
{
create
(
:group
)
}
let_it_be
(
:bulk_import
)
{
create
(
:bulk_import
,
user:
user
)
}
let_it_be
(
:entity
)
{
create
(
:bulk_import_entity
,
bulk_import:
bulk_import
,
group:
group
)
}
let_it_be
(
:tracker
)
{
create
(
:bulk_import_tracker
,
entity:
entity
)
}
let_it_be
(
:context
)
{
BulkImports
::
Pipeline
::
Context
.
new
(
tracker
)
}
subject
{
described_class
.
new
(
context
)
}
describe
'#run'
do
it
'maps existing users to the imported group'
do
first_page
=
extracted_data
(
email:
member_user1
.
email
,
has_next_page:
true
)
last_page
=
extracted_data
(
email:
member_user2
.
email
)
allow_next_instance_of
(
BulkImports
::
Common
::
Extractors
::
GraphqlExtractor
)
do
|
extractor
|
allow
(
extractor
)
.
to
receive
(
:extract
)
.
and_return
(
first_page
,
last_page
)
end
expect
{
subject
.
run
}.
to
change
(
GroupMember
,
:count
).
by
(
2
)
members
=
group
.
members
.
map
{
|
m
|
m
.
slice
(
:user_id
,
:access_level
)
}
expect
(
members
).
to
contain_exactly
(
{
user_id:
member_user1
.
id
,
access_level:
30
},
{
user_id:
member_user2
.
id
,
access_level:
30
}
)
end
end
describe
'#load'
do
it
'does nothing when there is no data'
do
expect
{
subject
.
load
(
context
,
nil
)
}.
not_to
change
(
GroupMember
,
:count
)
end
it
'creates the member'
do
data
=
{
'user_id'
=>
member_user1
.
id
,
'created_by_id'
=>
member_user2
.
id
,
'access_level'
=>
30
,
'created_at'
=>
'2020-01-01T00:00:00Z'
,
'updated_at'
=>
'2020-01-01T00:00:00Z'
,
'expires_at'
=>
nil
}
expect
{
subject
.
load
(
context
,
data
)
}.
to
change
(
GroupMember
,
:count
).
by
(
1
)
member
=
group
.
members
.
last
expect
(
member
.
user
).
to
eq
(
member_user1
)
expect
(
member
.
created_by
).
to
eq
(
member_user2
)
expect
(
member
.
access_level
).
to
eq
(
30
)
expect
(
member
.
created_at
).
to
eq
(
'2020-01-01T00:00:00Z'
)
expect
(
member
.
updated_at
).
to
eq
(
'2020-01-01T00:00:00Z'
)
expect
(
member
.
expires_at
).
to
eq
(
nil
)
end
context
'when user_id is current user id'
do
it
'does not create new member'
do
data
=
{
'user_id'
=>
user
.
id
}
expect
{
subject
.
load
(
context
,
data
)
}.
not_to
change
(
GroupMember
,
:count
)
end
end
end
describe
'pipeline parts'
do
it
{
expect
(
described_class
).
to
include_module
(
BulkImports
::
Pipeline
)
}
it
{
expect
(
described_class
).
to
include_module
(
BulkImports
::
Pipeline
::
Runner
)
}
it
'has extractors'
do
expect
(
described_class
.
get_extractor
)
.
to
eq
(
klass:
BulkImports
::
Common
::
Extractors
::
GraphqlExtractor
,
options:
{
query:
BulkImports
::
Groups
::
Graphql
::
GetMembersQuery
}
)
end
it
'has transformers'
do
expect
(
described_class
.
transformers
)
.
to
contain_exactly
(
{
klass:
BulkImports
::
Common
::
Transformers
::
ProhibitedAttributesTransformer
,
options:
nil
},
{
klass:
BulkImports
::
Groups
::
Transformers
::
MemberAttributesTransformer
,
options:
nil
}
)
end
end
def
extracted_data
(
email
:,
has_next_page:
false
)
data
=
{
'created_at'
=>
'2020-01-01T00:00:00Z'
,
'updated_at'
=>
'2020-01-01T00:00:00Z'
,
'expires_at'
=>
nil
,
'access_level'
=>
{
'integer_value'
=>
30
},
'user'
=>
{
'public_email'
=>
email
}
}
page_info
=
{
'has_next_page'
=>
has_next_page
,
'next_page'
=>
has_next_page
?
'cursor'
:
nil
}
BulkImports
::
Pipeline
::
ExtractedData
.
new
(
data:
data
,
page_info:
page_info
)
end
end
spec/lib/bulk_imports/groups/stage_spec.rb
View file @
3b671ccd
...
...
@@ -9,7 +9,7 @@ RSpec.describe BulkImports::Groups::Stage do
[
[
0
,
BulkImports
::
Groups
::
Pipelines
::
GroupPipeline
],
[
1
,
BulkImports
::
Groups
::
Pipelines
::
SubgroupEntitiesPipeline
],
[
1
,
BulkImports
::
Groups
::
Pipelines
::
MembersPipeline
],
[
1
,
BulkImports
::
Common
::
Pipelines
::
MembersPipeline
],
[
1
,
BulkImports
::
Common
::
Pipelines
::
LabelsPipeline
],
[
1
,
BulkImports
::
Common
::
Pipelines
::
MilestonesPipeline
],
[
1
,
BulkImports
::
Common
::
Pipelines
::
BadgesPipeline
],
...
...
spec/lib/bulk_imports/groups/transformers/member_attributes_transformer_spec.rb
View file @
3b671ccd
...
...
@@ -48,12 +48,12 @@ RSpec.describe BulkImports::Groups::Transformers::MemberAttributesTransformer do
data
=
member_data
(
email:
user
.
email
)
expect
(
subject
.
transform
(
context
,
data
)).
to
eq
(
'access_level'
=>
30
,
'user_id'
=>
user
.
id
,
'created_by_id'
=>
user
.
id
,
'created_at'
=>
'2020-01-01T00:00:00Z'
,
'updated_at'
=>
'2020-01-01T00:00:00Z'
,
'expires_at'
=>
nil
access_level:
30
,
user_id:
user
.
id
,
created_by_id:
user
.
id
,
created_at:
'2020-01-01T00:00:00Z'
,
updated_at:
'2020-01-01T00:00:00Z'
,
expires_at:
nil
)
end
...
...
@@ -62,12 +62,12 @@ RSpec.describe BulkImports::Groups::Transformers::MemberAttributesTransformer do
data
=
member_data
(
email:
secondary_email
)
expect
(
subject
.
transform
(
context
,
data
)).
to
eq
(
'access_level'
=>
30
,
'user_id'
=>
user
.
id
,
'created_by_id'
=>
user
.
id
,
'created_at'
=>
'2020-01-01T00:00:00Z'
,
'updated_at'
=>
'2020-01-01T00:00:00Z'
,
'expires_at'
=>
nil
access_level:
30
,
user_id:
user
.
id
,
created_by_id:
user
.
id
,
created_at:
'2020-01-01T00:00:00Z'
,
updated_at:
'2020-01-01T00:00:00Z'
,
expires_at:
nil
)
end
...
...
spec/lib/bulk_imports/projects/graphql/get_project_query_spec.rb
0 → 100644
View file @
3b671ccd
# frozen_string_literal: true
require
'spec_helper'
RSpec
.
describe
BulkImports
::
Projects
::
Graphql
::
GetProjectQuery
do
let_it_be
(
:tracker
)
{
create
(
:bulk_import_tracker
)
}
let_it_be
(
:context
)
{
BulkImports
::
Pipeline
::
Context
.
new
(
tracker
)
}
subject
(
:query
)
{
described_class
.
new
(
context:
context
)
}
it
'has a valid query'
do
parsed_query
=
GraphQL
::
Query
.
new
(
GitlabSchema
,
query
.
to_s
,
variables:
query
.
variables
)
result
=
GitlabSchema
.
static_validator
.
validate
(
parsed_query
)
expect
(
result
[
:errors
]).
to
be_empty
end
it
'queries project based on source_full_path'
do
expected
=
{
full_path:
tracker
.
entity
.
source_full_path
}
expect
(
subject
.
variables
).
to
eq
(
expected
)
end
end
spec/lib/bulk_imports/projects/graphql/get_repository_query_spec.rb
View file @
3b671ccd
...
...
@@ -3,19 +3,29 @@
require
'spec_helper'
RSpec
.
describe
BulkImports
::
Projects
::
Graphql
::
GetRepositoryQuery
do
describe
'query repository based on full_path'
do
let
(
:entity
)
{
double
(
source_full_path:
'test'
,
bulk_import:
nil
)
}
let
(
:tracker
)
{
double
(
entity:
entity
)
}
let
(
:context
)
{
BulkImports
::
Pipeline
::
Context
.
new
(
tracker
)
}
let_it_be
(
:tracker
)
{
create
(
:bulk_import_tracker
)
}
let_it_be
(
:context
)
{
BulkImports
::
Pipeline
::
Context
.
new
(
tracker
)
}
it
'returns project repository url'
do
expect
(
described_class
.
to_s
).
to
include
(
'httpUrlToRepo'
)
end
subject
(
:query
)
{
described_class
.
new
(
context:
context
)
}
it
'queries project based on source_full_path'
do
expected
=
{
full_path:
entity
.
source_full_path
}
it
'has a valid query'
do
parsed_query
=
GraphQL
::
Query
.
new
(
GitlabSchema
,
query
.
to_s
,
variables:
query
.
variables
)
result
=
GitlabSchema
.
static_validator
.
validate
(
parsed_query
)
expect
(
described_class
.
variables
(
context
)).
to
eq
(
expected
)
end
expect
(
result
[
:errors
]).
to
be_empty
end
it
'returns project repository url'
do
expect
(
subject
.
to_s
).
to
include
(
'httpUrlToRepo'
)
end
it
'queries project based on source_full_path'
do
expected
=
{
full_path:
tracker
.
entity
.
source_full_path
}
expect
(
subject
.
variables
).
to
eq
(
expected
)
end
end
spec/lib/bulk_imports/projects/graphql/get_snippet_repository_query_spec.rb
View file @
3b671ccd
...
...
@@ -3,56 +3,56 @@
require
'spec_helper'
RSpec
.
describe
BulkImports
::
Projects
::
Graphql
::
GetSnippetRepositoryQuery
do
describe
'query repository based on full_path'
do
let_it_be
(
:entity
)
{
create
(
:bulk_import_entity
)
}
let_it_be
(
:tracker
)
{
create
(
:bulk_import_tracker
,
entity:
entity
)
}
let_it_be
(
:context
)
{
BulkImports
::
Pipeline
::
Context
.
new
(
tracker
)
}
it
'has a valid query'
do
query
=
GraphQL
::
Query
.
new
(
GitlabSchema
,
described_class
.
to_s
,
variables:
described_class
.
variables
(
context
)
)
result
=
GitlabSchema
.
static_validator
.
validate
(
query
)
expect
(
result
[
:errors
]).
to
be_empty
end
let_it_be
(
:entity
)
{
create
(
:bulk_import_entity
)
}
let_it_be
(
:tracker
)
{
create
(
:bulk_import_tracker
,
entity:
entity
)
}
let_it_be
(
:context
)
{
BulkImports
::
Pipeline
::
Context
.
new
(
tracker
)
}
it
'returns snippet httpUrlToRepo'
do
expect
(
described_class
.
to_s
).
to
include
(
'httpUrlToRepo'
)
end
subject
(
:query
)
{
described_class
.
new
(
context:
context
)
}
it
'returns snippet createdAt'
do
expect
(
described_class
.
to_s
).
to
include
(
'createdAt'
)
end
it
'has a valid query'
do
parsed_query
=
GraphQL
::
Query
.
new
(
GitlabSchema
,
query
.
to_s
,
variables:
query
.
variables
)
result
=
GitlabSchema
.
static_validator
.
validate
(
parsed_query
)
it
'returns snippet title'
do
expect
(
described_class
.
to_s
).
to
include
(
'title'
)
end
expect
(
result
[
:errors
]).
to
be_empty
end
describe
'.variables
'
do
it
'queries project based on source_full_path and pagination'
do
expected
=
{
full_path:
entity
.
source_full_path
,
cursor:
nil
,
per_page:
500
}
it
'returns snippet httpUrlToRepo
'
do
expect
(
subject
.
to_s
).
to
include
(
'httpUrlToRepo'
)
end
expect
(
described_class
.
variables
(
context
)).
to
eq
(
expected
)
end
it
'returns snippet createdAt'
do
expect
(
subject
.
to_s
).
to
include
(
'createdAt'
)
end
it
'returns snippet title'
do
expect
(
subject
.
to_s
).
to
include
(
'title'
)
end
describe
'.variables'
do
it
'queries project based on source_full_path and pagination'
do
expected
=
{
full_path:
entity
.
source_full_path
,
cursor:
nil
,
per_page:
500
}
expect
(
subject
.
variables
).
to
eq
(
expected
)
end
end
describe
'.data_path'
do
it
'.data_path returns data path'
do
expected
=
%w[data project snippets nodes]
describe
'.data_path'
do
it
'.data_path returns data path'
do
expected
=
%w[data project snippets nodes]
expect
(
described_class
.
data_path
).
to
eq
(
expected
)
end
expect
(
subject
.
data_path
).
to
eq
(
expected
)
end
end
describe
'.page_info_path'
do
it
'.page_info_path returns pagination information path'
do
expected
=
%w[data project snippets page_info]
describe
'.page_info_path'
do
it
'.page_info_path returns pagination information path'
do
expected
=
%w[data project snippets page_info]
expect
(
described_class
.
page_info_path
).
to
eq
(
expected
)
end
expect
(
subject
.
page_info_path
).
to
eq
(
expected
)
end
end
end
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment