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
c1a4a20c
Commit
c1a4a20c
authored
Feb 04, 2020
by
Fabio Pitino
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Port Ci::Bridge to Core
Move most of the model and specs
parent
54c2a03b
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
351 additions
and
385 deletions
+351
-385
app/models/ci/bridge.rb
app/models/ci/bridge.rb
+121
-1
ee/app/models/ee/ci/bridge.rb
ee/app/models/ee/ci/bridge.rb
+0
-133
ee/spec/models/ci/bridge_spec.rb
ee/spec/models/ci/bridge_spec.rb
+0
-250
spec/models/ci/bridge_spec.rb
spec/models/ci/bridge_spec.rb
+230
-1
No files found.
app/models/ci/bridge.rb
View file @
c1a4a20c
...
...
@@ -4,19 +4,78 @@ module Ci
class
Bridge
<
Ci
::
Processable
include
Ci
::
Contextable
include
Ci
::
PipelineDelegator
include
Ci
::
Metadatable
include
Importable
include
AfterCommitQueue
include
HasRef
include
Gitlab
::
Utils
::
StrongMemoize
InvalidBridgeTypeError
=
Class
.
new
(
StandardError
)
belongs_to
:project
belongs_to
:trigger_request
has_many
:sourced_pipelines
,
class_name:
"::Ci::Sources::Pipeline"
,
foreign_key: :source_job_id
validates
:ref
,
presence:
true
# rubocop:disable Cop/ActiveRecordSerialize
serialize
:options
serialize
:yaml_variables
,
::
Gitlab
::
Serializer
::
Ci
::
Variables
# rubocop:enable Cop/ActiveRecordSerialize
state_machine
:status
do
event
:manual
do
transition
all
=>
:manual
end
event
:scheduled
do
transition
all
=>
:scheduled
end
end
def
self
.
retry
(
bridge
,
current_user
)
raise
NotImplementedError
end
def
inherit_status_from_downstream!
(
pipeline
)
case
pipeline
.
status
when
'success'
self
.
success!
when
'failed'
,
'canceled'
,
'skipped'
self
.
drop!
else
false
end
end
def
downstream_pipeline_params
return
child_params
if
triggers_child_pipeline?
return
cross_project_params
if
downstream_project
.
present?
{}
end
def
downstream_project
strong_memoize
(
:downstream_project
)
do
if
downstream_project_path
::
Project
.
find_by_full_path
(
downstream_project_path
)
elsif
triggers_child_pipeline?
project
end
end
end
def
downstream_project_path
strong_memoize
(
:downstream_project_path
)
do
options
&
.
dig
(
:trigger
,
:project
)
end
end
def
triggers_child_pipeline?
yaml_for_downstream
.
present?
end
def
tags
[
:bridge
]
end
...
...
@@ -55,7 +114,68 @@ module Ci
end
def
yaml_for_downstream
nil
strong_memoize
(
:yaml_for_downstream
)
do
includes
=
options
&
.
dig
(
:trigger
,
:include
)
YAML
.
dump
(
'include'
=>
includes
)
if
includes
end
end
def
target_ref
branch
=
options
&
.
dig
(
:trigger
,
:branch
)
return
unless
branch
scoped_variables
.
to_runner_variables
.
yield_self
do
|
all_variables
|
::
ExpandVariables
.
expand
(
branch
,
all_variables
)
end
end
def
dependent?
strong_memoize
(
:dependent
)
do
options
&
.
dig
(
:trigger
,
:strategy
)
==
'depend'
end
end
def
downstream_variables
variables
=
scoped_variables
.
concat
(
pipeline
.
persisted_variables
)
variables
.
to_runner_variables
.
yield_self
do
|
all_variables
|
yaml_variables
.
to_a
.
map
do
|
hash
|
{
key:
hash
[
:key
],
value:
::
ExpandVariables
.
expand
(
hash
[
:value
],
all_variables
)
}
end
end
end
private
def
cross_project_params
{
project:
downstream_project
,
source: :pipeline
,
target_revision:
{
ref:
target_ref
||
downstream_project
.
default_branch
},
execute_params:
{
ignore_skip_ci:
true
}
}
end
def
child_params
parent_pipeline
=
pipeline
{
project:
project
,
source: :parent_pipeline
,
target_revision:
{
ref:
parent_pipeline
.
ref
,
checkout_sha:
parent_pipeline
.
sha
,
before:
parent_pipeline
.
before_sha
,
source_sha:
parent_pipeline
.
source_sha
,
target_sha:
parent_pipeline
.
target_sha
},
execute_params:
{
ignore_skip_ci:
true
,
bridge:
self
}
}
end
end
end
...
...
ee/app/models/ee/ci/bridge.rb
View file @
c1a4a20c
...
...
@@ -4,22 +4,9 @@ module EE
module
Ci
module
Bridge
extend
ActiveSupport
::
Concern
extend
::
Gitlab
::
Utils
::
Override
include
::
Gitlab
::
Utils
::
StrongMemoize
InvalidBridgeTypeError
=
Class
.
new
(
StandardError
)
prepended
do
include
::
Ci
::
Metadatable
# rubocop:disable Cop/ActiveRecordSerialize
serialize
:options
serialize
:yaml_variables
,
::
Gitlab
::
Serializer
::
Ci
::
Variables
# rubocop:enable Cop/ActiveRecordSerialize
belongs_to
:upstream_pipeline
,
class_name:
"::Ci::Pipeline"
has_many
:sourced_pipelines
,
class_name:
"::Ci::Sources::Pipeline"
,
foreign_key: :source_job_id
state_machine
:status
do
after_transition
created: :pending
do
|
bridge
|
...
...
@@ -37,14 +24,6 @@ module EE
bridge
.
subscribe_to_upstream!
end
end
event
:manual
do
transition
all
=>
:manual
end
event
:scheduled
do
transition
all
=>
:scheduled
end
end
end
...
...
@@ -84,129 +63,17 @@ module EE
end
end
def
inherit_status_from_downstream!
(
pipeline
)
case
pipeline
.
status
when
'success'
self
.
success!
when
'failed'
,
'canceled'
,
'skipped'
self
.
drop!
else
false
end
end
def
target_user
self
.
user
end
def
target_project
downstream_project
||
upstream_project
end
def
triggers_child_pipeline?
yaml_for_downstream
.
present?
end
override
:yaml_for_downstream
def
yaml_for_downstream
strong_memoize
(
:yaml_for_downstream
)
do
includes
=
options
&
.
dig
(
:trigger
,
:include
)
YAML
.
dump
(
'include'
=>
includes
)
if
includes
end
end
def
downstream_pipeline_params
return
child_params
if
triggers_child_pipeline?
return
cross_project_params
if
downstream_project
.
present?
{}
end
def
downstream_project
strong_memoize
(
:downstream_project
)
do
if
downstream_project_path
::
Project
.
find_by_full_path
(
downstream_project_path
)
elsif
triggers_child_pipeline?
project
end
end
end
def
upstream_project
strong_memoize
(
:upstream_project
)
do
upstream_project_path
&&
::
Project
.
find_by_full_path
(
upstream_project_path
)
end
end
def
target_ref
branch
=
options
&
.
dig
(
:trigger
,
:branch
)
return
unless
branch
scoped_variables
.
to_runner_variables
.
yield_self
do
|
all_variables
|
::
ExpandVariables
.
expand
(
branch
,
all_variables
)
end
end
def
dependent?
strong_memoize
(
:dependent
)
do
options
&
.
dig
(
:trigger
,
:strategy
)
==
'depend'
end
end
def
downstream_variables
variables
=
scoped_variables
.
concat
(
pipeline
.
persisted_variables
)
variables
.
to_runner_variables
.
yield_self
do
|
all_variables
|
yaml_variables
.
to_a
.
map
do
|
hash
|
{
key:
hash
[
:key
],
value:
::
ExpandVariables
.
expand
(
hash
[
:value
],
all_variables
)
}
end
end
end
def
downstream_project_path
strong_memoize
(
:downstream_project_path
)
do
options
&
.
dig
(
:trigger
,
:project
)
end
end
def
upstream_project_path
strong_memoize
(
:upstream_project_path
)
do
options
&
.
dig
(
:bridge_needs
,
:pipeline
)
end
end
private
def
cross_project_params
{
project:
downstream_project
,
source: :pipeline
,
target_revision:
{
ref:
target_ref
||
downstream_project
.
default_branch
},
execute_params:
{
ignore_skip_ci:
true
}
}
end
def
child_params
parent_pipeline
=
pipeline
{
project:
project
,
source: :parent_pipeline
,
target_revision:
{
ref:
parent_pipeline
.
ref
,
checkout_sha:
parent_pipeline
.
sha
,
before:
parent_pipeline
.
before_sha
,
source_sha:
parent_pipeline
.
source_sha
,
target_sha:
parent_pipeline
.
target_sha
},
execute_params:
{
ignore_skip_ci:
true
,
bridge:
self
}
}
end
end
end
end
ee/spec/models/ci/bridge_spec.rb
View file @
c1a4a20c
...
...
@@ -21,10 +21,6 @@ describe Ci::Bridge do
expect
(
bridge
).
to
belong_to
(
:upstream_pipeline
)
end
it
'has many sourced pipelines'
do
expect
(
bridge
).
to
have_many
(
:sourced_pipelines
)
end
describe
'state machine transitions'
do
context
'when bridge points towards downstream'
do
it
'does not subscribe to upstream project'
do
...
...
@@ -32,12 +28,6 @@ describe Ci::Bridge do
bridge
.
enqueue!
end
it
'schedules downstream pipeline creation'
do
expect
(
bridge
).
to
receive
(
:schedule_downstream_pipeline!
)
bridge
.
enqueue!
end
end
context
'when bridge points towards upstream'
do
...
...
@@ -107,244 +97,4 @@ describe Ci::Bridge do
end
end
end
describe
'#inherit_status_from_downstream!'
do
let
(
:downstream_pipeline
)
{
build
(
:ci_pipeline
,
status:
downstream_status
)
}
before
do
bridge
.
status
=
'pending'
create
(
:ci_sources_pipeline
,
pipeline:
downstream_pipeline
,
source_job:
bridge
)
end
subject
{
bridge
.
inherit_status_from_downstream!
(
downstream_pipeline
)
}
context
'when status is not supported'
do
(
::
Ci
::
Pipeline
::
AVAILABLE_STATUSES
-
::
Ci
::
Pipeline
::
COMPLETED_STATUSES
).
map
(
&
:to_s
).
each
do
|
status
|
context
"when status is
#{
status
}
"
do
let
(
:downstream_status
)
{
status
}
it
'returns false'
do
expect
(
subject
).
to
eq
(
false
)
end
it
'does not change the bridge status'
do
expect
{
subject
}.
not_to
change
{
bridge
.
status
}.
from
(
'pending'
)
end
end
end
end
context
'when status is supported'
do
using
RSpec
::
Parameterized
::
TableSyntax
where
(
:downstream_status
,
:upstream_status
)
do
[
%w[success success]
,
*::
Ci
::
Pipeline
.
completed_statuses
.
without
(
:success
).
map
{
|
status
|
[
status
.
to_s
,
'failed'
]
}
]
end
with_them
do
it
'inherits the downstream status'
do
expect
{
subject
}.
to
change
{
bridge
.
status
}.
from
(
'pending'
).
to
(
upstream_status
)
end
end
end
end
describe
'#target_user'
do
it
'is the same as a user who created a pipeline'
do
expect
(
bridge
.
target_user
).
to
eq
bridge
.
user
end
end
describe
'#target_project'
do
context
'when trigger is defined'
do
it
'returns a full path of a project'
do
expect
(
bridge
.
target_project
).
to
eq
target_project
end
end
context
'when trigger does not have project defined'
do
let
(
:options
)
{
{
trigger:
{}
}
}
it
'returns nil'
do
expect
(
bridge
.
target_project
).
to
be_nil
end
end
end
describe
'#target_ref'
do
context
'when trigger is defined'
do
it
'returns a ref name'
do
expect
(
bridge
.
target_ref
).
to
eq
'master'
end
context
'when using variable expansion'
do
let
(
:options
)
{
{
trigger:
{
project:
'my/project'
,
branch:
'$BRIDGE-master'
}
}
}
it
'correctly expands variables'
do
expect
(
bridge
.
target_ref
).
to
eq
(
'cross-master'
)
end
end
end
context
'when trigger does not have project defined'
do
let
(
:options
)
{
nil
}
it
'returns nil'
do
expect
(
bridge
.
target_ref
).
to
be_nil
end
end
end
describe
'#dependent?'
do
subject
{
bridge
.
dependent?
}
context
'when bridge has strategy depend'
do
let
(
:options
)
{
{
trigger:
{
project:
'my/project'
,
strategy:
'depend'
}
}
}
it
{
is_expected
.
to
be
true
}
end
context
'when bridge does not have strategy depend'
do
it
{
is_expected
.
to
be
false
}
end
end
describe
'#yaml_variables'
do
it
'returns YAML variables'
do
expect
(
bridge
.
yaml_variables
)
.
to
include
(
key:
'BRIDGE'
,
value:
'cross'
,
public:
true
)
end
end
describe
'#downstream_variables'
do
it
'returns variables that are going to be passed downstream'
do
expect
(
bridge
.
downstream_variables
)
.
to
include
(
key:
'BRIDGE'
,
value:
'cross'
)
end
context
'when using variables interpolation'
do
let
(
:yaml_variables
)
do
[
{
key:
'EXPANDED'
,
value:
'$BRIDGE-bridge'
,
public:
true
},
{
key:
'UPSTREAM_CI_PIPELINE_ID'
,
value:
'$CI_PIPELINE_ID'
,
public:
true
},
{
key:
'UPSTREAM_CI_PIPELINE_URL'
,
value:
'$CI_PIPELINE_URL'
,
public:
true
}
]
end
before
do
bridge
.
yaml_variables
.
concat
(
yaml_variables
)
end
it
'correctly expands variables with interpolation'
do
expanded_values
=
pipeline
.
persisted_variables
.
to_hash
.
transform_keys
{
|
key
|
"UPSTREAM_
#{
key
}
"
}
.
map
{
|
key
,
value
|
{
key:
key
,
value:
value
}
}
.
push
(
key:
'EXPANDED'
,
value:
'cross-bridge'
)
expect
(
bridge
.
downstream_variables
)
.
to
match
(
a_collection_including
(
*
expanded_values
))
end
end
context
'when recursive interpolation has been used'
do
before
do
bridge
.
yaml_variables
<<
{
key:
'EXPANDED'
,
value:
'$EXPANDED'
,
public:
true
}
end
it
'does not expand variable recursively'
do
expect
(
bridge
.
downstream_variables
)
.
to
include
(
key:
'EXPANDED'
,
value:
'$EXPANDED'
)
end
end
end
describe
'metadata support'
do
it
'reads YAML variables from metadata'
do
expect
(
bridge
.
yaml_variables
).
not_to
be_empty
expect
(
bridge
.
metadata
).
to
be_a
Ci
::
BuildMetadata
expect
(
bridge
.
read_attribute
(
:yaml_variables
)).
to
be_nil
expect
(
bridge
.
metadata
.
config_variables
).
to
be
bridge
.
yaml_variables
end
it
'reads options from metadata'
do
expect
(
bridge
.
options
).
not_to
be_empty
expect
(
bridge
.
metadata
).
to
be_a
Ci
::
BuildMetadata
expect
(
bridge
.
read_attribute
(
:options
)).
to
be_nil
expect
(
bridge
.
metadata
.
config_options
).
to
be
bridge
.
options
end
end
describe
'#triggers_child_pipeline?'
do
subject
{
bridge
.
triggers_child_pipeline?
}
context
'when bridge defines a downstream YAML'
do
let
(
:options
)
do
{
trigger:
{
include:
'path/to/child.yml'
}
}
end
it
{
is_expected
.
to
be_truthy
}
end
context
'when bridge does not define a downstream YAML'
do
let
(
:options
)
do
{
trigger:
{
project:
project
.
full_path
}
}
end
it
{
is_expected
.
to
be_falsey
}
end
end
describe
'#yaml_for_downstream'
do
subject
{
bridge
.
yaml_for_downstream
}
context
'when bridge defines a downstream YAML'
do
let
(
:options
)
do
{
trigger:
{
include:
'path/to/child.yml'
}
}
end
let
(
:yaml
)
do
<<~
EOY
---
include: path/to/child.yml
EOY
end
it
{
is_expected
.
to
eq
yaml
}
end
context
'when bridge does not define a downstream YAML'
do
let
(
:options
)
{
{}
}
it
{
is_expected
.
to
be_nil
}
end
end
end
spec/models/ci/bridge_spec.rb
View file @
c1a4a20c
...
...
@@ -4,14 +4,25 @@ require 'spec_helper'
describe
Ci
::
Bridge
do
set
(
:project
)
{
create
(
:project
)
}
set
(
:target_project
)
{
create
(
:project
,
name:
'project'
,
namespace:
create
(
:namespace
,
name:
'my'
))
}
set
(
:pipeline
)
{
create
(
:ci_pipeline
,
project:
project
)
}
let
(
:bridge
)
do
create
(
:ci_bridge
,
pipeline:
pipeline
)
create
(
:ci_bridge
,
:variables
,
status: :created
,
options:
options
,
pipeline:
pipeline
)
end
let
(
:options
)
do
{
trigger:
{
project:
'my/project'
,
branch:
'master'
}
}
end
it
{
is_expected
.
to
include_module
(
Ci
::
PipelineDelegator
)
}
it
'has many sourced pipelines'
do
expect
(
bridge
).
to
have_many
(
:sourced_pipelines
)
end
describe
'#tags'
do
it
'only has a bridge tag'
do
expect
(
bridge
.
tags
).
to
eq
[
:bridge
]
...
...
@@ -41,4 +52,222 @@ describe Ci::Bridge do
expect
(
bridge
.
scoped_variables_hash
.
keys
).
to
include
(
*
variables
)
end
end
describe
'#inherit_status_from_downstream!'
do
let
(
:downstream_pipeline
)
{
build
(
:ci_pipeline
,
status:
downstream_status
)
}
before
do
bridge
.
status
=
'pending'
create
(
:ci_sources_pipeline
,
pipeline:
downstream_pipeline
,
source_job:
bridge
)
end
subject
{
bridge
.
inherit_status_from_downstream!
(
downstream_pipeline
)
}
context
'when status is not supported'
do
(
::
Ci
::
Pipeline
::
AVAILABLE_STATUSES
-
::
Ci
::
Pipeline
::
COMPLETED_STATUSES
).
map
(
&
:to_s
).
each
do
|
status
|
context
"when status is
#{
status
}
"
do
let
(
:downstream_status
)
{
status
}
it
'returns false'
do
expect
(
subject
).
to
eq
(
false
)
end
it
'does not change the bridge status'
do
expect
{
subject
}.
not_to
change
{
bridge
.
status
}.
from
(
'pending'
)
end
end
end
end
context
'when status is supported'
do
using
RSpec
::
Parameterized
::
TableSyntax
where
(
:downstream_status
,
:upstream_status
)
do
[
%w[success success]
,
*::
Ci
::
Pipeline
.
completed_statuses
.
without
(
:success
).
map
{
|
status
|
[
status
.
to_s
,
'failed'
]
}
]
end
with_them
do
it
'inherits the downstream status'
do
expect
{
subject
}.
to
change
{
bridge
.
status
}.
from
(
'pending'
).
to
(
upstream_status
)
end
end
end
end
describe
'#dependent?'
do
subject
{
bridge
.
dependent?
}
context
'when bridge has strategy depend'
do
let
(
:options
)
{
{
trigger:
{
project:
'my/project'
,
strategy:
'depend'
}
}
}
it
{
is_expected
.
to
be
true
}
end
context
'when bridge does not have strategy depend'
do
it
{
is_expected
.
to
be
false
}
end
end
describe
'#yaml_variables'
do
it
'returns YAML variables'
do
expect
(
bridge
.
yaml_variables
)
.
to
include
(
key:
'BRIDGE'
,
value:
'cross'
,
public:
true
)
end
end
describe
'#downstream_variables'
do
it
'returns variables that are going to be passed downstream'
do
expect
(
bridge
.
downstream_variables
)
.
to
include
(
key:
'BRIDGE'
,
value:
'cross'
)
end
context
'when using variables interpolation'
do
let
(
:yaml_variables
)
do
[
{
key:
'EXPANDED'
,
value:
'$BRIDGE-bridge'
,
public:
true
},
{
key:
'UPSTREAM_CI_PIPELINE_ID'
,
value:
'$CI_PIPELINE_ID'
,
public:
true
},
{
key:
'UPSTREAM_CI_PIPELINE_URL'
,
value:
'$CI_PIPELINE_URL'
,
public:
true
}
]
end
before
do
bridge
.
yaml_variables
.
concat
(
yaml_variables
)
end
it
'correctly expands variables with interpolation'
do
expanded_values
=
pipeline
.
persisted_variables
.
to_hash
.
transform_keys
{
|
key
|
"UPSTREAM_
#{
key
}
"
}
.
map
{
|
key
,
value
|
{
key:
key
,
value:
value
}
}
.
push
(
key:
'EXPANDED'
,
value:
'cross-bridge'
)
expect
(
bridge
.
downstream_variables
)
.
to
match
(
a_collection_including
(
*
expanded_values
))
end
end
context
'when recursive interpolation has been used'
do
before
do
bridge
.
yaml_variables
<<
{
key:
'EXPANDED'
,
value:
'$EXPANDED'
,
public:
true
}
end
it
'does not expand variable recursively'
do
expect
(
bridge
.
downstream_variables
)
.
to
include
(
key:
'EXPANDED'
,
value:
'$EXPANDED'
)
end
end
end
describe
'metadata support'
do
it
'reads YAML variables from metadata'
do
expect
(
bridge
.
yaml_variables
).
not_to
be_empty
expect
(
bridge
.
metadata
).
to
be_a
Ci
::
BuildMetadata
expect
(
bridge
.
read_attribute
(
:yaml_variables
)).
to
be_nil
expect
(
bridge
.
metadata
.
config_variables
).
to
be
bridge
.
yaml_variables
end
it
'reads options from metadata'
do
expect
(
bridge
.
options
).
not_to
be_empty
expect
(
bridge
.
metadata
).
to
be_a
Ci
::
BuildMetadata
expect
(
bridge
.
read_attribute
(
:options
)).
to
be_nil
expect
(
bridge
.
metadata
.
config_options
).
to
be
bridge
.
options
end
end
describe
'#triggers_child_pipeline?'
do
subject
{
bridge
.
triggers_child_pipeline?
}
context
'when bridge defines a downstream YAML'
do
let
(
:options
)
do
{
trigger:
{
include:
'path/to/child.yml'
}
}
end
it
{
is_expected
.
to
be_truthy
}
end
context
'when bridge does not define a downstream YAML'
do
let
(
:options
)
do
{
trigger:
{
project:
project
.
full_path
}
}
end
it
{
is_expected
.
to
be_falsey
}
end
end
describe
'#yaml_for_downstream'
do
subject
{
bridge
.
yaml_for_downstream
}
context
'when bridge defines a downstream YAML'
do
let
(
:options
)
do
{
trigger:
{
include:
'path/to/child.yml'
}
}
end
let
(
:yaml
)
do
<<~
EOY
---
include: path/to/child.yml
EOY
end
it
{
is_expected
.
to
eq
yaml
}
end
context
'when bridge does not define a downstream YAML'
do
let
(
:options
)
{
{}
}
it
{
is_expected
.
to
be_nil
}
end
end
describe
'#target_ref'
do
context
'when trigger is defined'
do
it
'returns a ref name'
do
expect
(
bridge
.
target_ref
).
to
eq
'master'
end
context
'when using variable expansion'
do
let
(
:options
)
{
{
trigger:
{
project:
'my/project'
,
branch:
'$BRIDGE-master'
}
}
}
it
'correctly expands variables'
do
expect
(
bridge
.
target_ref
).
to
eq
(
'cross-master'
)
end
end
end
context
'when trigger does not have project defined'
do
let
(
:options
)
{
nil
}
it
'returns nil'
do
expect
(
bridge
.
target_ref
).
to
be_nil
end
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