anchor.diff:  
   
   %% @private
  -encode_metadata(Metadata) when is_binary(Metadata) ->
  -    Metadata;
  -encode_metadata(Metadata) when is_map(Metadata) ->
  -    json_utils:encode(Metadata).
  -
  -
  -%% @private
   maybe_decode_json(MaybeEncodedJson) ->
       try
           json_utils:decode(MaybeEncodedJson)
  
positive.code:     end.
  
  
  set_json_metadata_test(Config) ->
      [Provider2, Provider1] = Providers = ?config(op_worker_nodes, Config),
  
      GetSessionFun = fun(Node) ->
          ?config({session_id, {?USER_IN_BOTH_SPACES, ?GET_DOMAIN(Node)}}, Config)
      end,
  
      UserSessId = GetSessionFun(Provider2),
  
      RootDirPath = filename:join(["/", ?SPACE_2, ?SCENARIO_NAME]),
      {ok, RootDirGuid} = lfm_proxy:mkdir(Provider2, UserSessId, RootDirPath, 8#777),
      {ok, ShareId} = lfm_proxy:create_share(Provider2, UserSessId, {guid, RootDirGuid}, <<"share">>),
  
      DirPath = filename:join([RootDirPath, <<"dir">>]),
      {ok, DirGuid} = lfm_proxy:mkdir(Provider2, UserSessId, DirPath, 8#777),
  
      RegularFilePath = filename:join([RootDirPath, <<"file">>]),
      {ok, RegularFileGuid} = lfm_proxy:create(Provider2, UserSessId, RegularFilePath, 8#777),
  
      % Wait for metadata sync between providers
      ?assertMatch(
          {ok, #file_attr{}},
          lfm_proxy:stat(Provider1, GetSessionFun(Provider1), {guid, RegularFileGuid}),
          ?ATTEMPTS
      ),
  
      GetMetadataFun = fun(Node, FileGuid) ->
          SessId = GetSessionFun(Node),
          lfm_proxy:get_metadata(Node, SessId, {guid, FileGuid}, json, [], false)
      end,
      RemoveMetadataFun = fun(Node, FileGuid) ->
          SessId = GetSessionFun(Node),
          lfm_proxy:remove_metadata(Node, SessId, {guid, FileGuid}, json)
      end,
  
      ExampleJson = #{<<"attr1">> => [0, 1, <<"val">>]},
  
      DataSpec = #data_spec{
          required = [<<"metadata">>],
          optional = [<<"filter_type">>, <<"filter">>],
          correct_values = #{
              <<"metadata">> => [ExampleJson],
              <<"filter_type">> => [<<"keypath">>],
              <<"filter">> => [
                  <<"attr1.[1]">>,        % Test setting attr in existing array
                  <<"attr1.[2].attr22">>, % Test error when trying to set subjson to binary (<<"val">> in ExampleJson)
                  <<"attr1.[5]">>,        % Test setting attr beyond existing array
                  <<"attr2.[2]">>         % Test setting attr in nonexistent array
              ]
          },
          bad_values = [
              % invalid json error can be returned only for rest (invalid json is send as
              % body without modification) and not gs (#{<<"metadata">> => some_binary} is send,
              % so no matter what that some_binary is it will be treated as string)
              {<<"metadata">>, <<"aaa">>, {rest_handler, ?ERROR_BAD_VALUE_JSON(<<"metadata">>)}},
              {<<"metadata">>, <<"{">>, {rest_handler, ?ERROR_BAD_VALUE_JSON(<<"metadata">>)}},
              {<<"metadata">>, <<"{\"aaa\": aaa}">>, {rest_handler, ?ERROR_BAD_VALUE_JSON(<<"metadata">>)}},
  
              {<<"filter_type">>, <<"dummy">>, ?ERROR_BAD_VALUE_NOT_ALLOWED(<<"filter_type">>, [<<"keypath">>])},
  
              % Below differences between error returned by rest and gs are results of sending
              % parameters via qs in REST, so they lost their original type and are cast to binary
              {<<"filter_type">>, 100, {rest, ?ERROR_BAD_VALUE_NOT_ALLOWED(<<"filter_type">>, [<<"keypath">>])}},
              {<<"filter_type">>, 100, {gs, ?ERROR_BAD_VALUE_BINARY(<<"filter_type">>)}},
              {<<"filter">>, 100, {gs, ?ERROR_BAD_VALUE_BINARY(<<"filter">>)}}
          ]
      },
  
      GetExpectedResultAndFiltersFun = fun(#api_test_ctx{data = Data}) ->
          FilterType = maps:get(<<"filter_type">>, Data, undefined),
          Filter = maps:get(<<"filter">>, Data, undefined),
  
          case {FilterType, Filter} of
              {undefined, _} ->
                  {ok, []};
              {<<"keypath">>, undefined} ->
                  ?ERROR_MISSING_REQUIRED_VALUE(<<"filter">>);
              {<<"keypath">>, _} ->
                  case binary:split(Filter, <<".">>, [global]) of
                      [<<"attr1">>, <<"[2]">>, <<"attr22">>] ->
                          ?ERROR_POSIX(?ENODATA);
                      ExistingPath ->
                          {ok, ExistingPath}
                  end
          end
      end,
      ConstructVerifyEnvForSuccessfulCallsFun = fun(FileGuid) -> fun
          (false, #api_test_ctx{node = Node}) ->
              ?assertMatch({error, ?ENODATA}, GetMetadataFun(Node, FileGuid), ?ATTEMPTS),
              true;
          (true, #api_test_ctx{node = TestNode} = TestCtx) ->
              ExpResult = GetExpectedResultAndFiltersFun(TestCtx),
              lists:foreach(fun(Node) ->
                  % Below expected metadata depends on the tested parameters combination order.
                  % First only required params will be tested, then with only one optional params,
                  % next with 2 and so on. If optional param has multiple values then those later
                  % will be also tested later.
                  ExpJson = case ExpResult of
                      {ok, []} ->
                          ExampleJson;
                      ?ERROR_MISSING_REQUIRED_VALUE(_) ->
                          % Test failed to override previously set json because of specifying
                          % filter_type without specifying filter
                          ExampleJson;
                      {ok, [<<"attr1">>, <<"[1]">>]} ->
                          #{<<"attr1">> => [0, ExampleJson, <<"val">>]};
                      ?ERROR_POSIX(?ENODATA) ->
                          % Operation failed and nothing should be changed -
                          % it should match the same json as above
                          #{<<"attr1">> => [0, ExampleJson, <<"val">>]};
                      {ok, [<<"attr1">>, <<"[5]">>]} ->
                          #{<<"attr1">> => [0, ExampleJson, <<"val">>, null, null, ExampleJson]};
                      {ok, [<<"attr2">>, <<"[2]">>]} ->
                          #{
                              <<"attr1">> => [0, ExampleJson, <<"val">>, null, null, ExampleJson],
                              <<"attr2">> => [null, null, ExampleJson]
                          }
                  end,
                  ?assertMatch({ok, ExpJson}, GetMetadataFun(Node, FileGuid), ?ATTEMPTS)
              end, Providers),
  
              case ExpResult of
                  {ok, [<<"attr2">>, <<"[2]">>]} ->
                      % Remove metadata after last successful parameters combination tested so that
                      % next tests can start from setting rather then updating metadata
                      ?assertMatch(ok, RemoveMetadataFun(TestNode, FileGuid)),
                      % Wait for changes to be synced between providers. Otherwise it can possible
                      % interfere with tests on other node (e.g. information about deletion that
                      % comes after setting ExampleJson and before setting using filter results in
                      % json metadata removal. In such case next test using 'filter' parameter should expect
                      % ExpMetadata = #{<<"attr1">> => [null, null, null, null, null, ExampleJson]}
                      % rather than above one as that will be the result of setting ExampleJson
                      % with attr1.[5] filter and no prior json set)
                      lists:foreach(fun(Node) ->
                          ?assertMatch({error, ?ENODATA}, GetMetadataFun(Node, FileGuid), ?ATTEMPTS)
                      end, Providers);
                  _ ->
                      ok
              end,
              true
      end end,
  
      set_metadata_test_base(
          Config,
          <<"json">>,
  
          GetMetadataFun,
          RemoveMetadataFun,
          fun(TestCtx) ->
              case GetExpectedResultAndFiltersFun(TestCtx) of
                  {ok, _} ->
                      % Filters are not needed in 'set_metadata_test_base' - what
                      % is needed is only information whether call should succeed
                      % or fail with specific error
                      ok;
                  {error, _} = Error ->
                      Error
              end
          end,
          ConstructVerifyEnvForSuccessfulCallsFun,
  
          DataSpec,
          ShareId,
          [
              {<<"dir">>, DirPath, DirGuid},
              {<<"file">>, RegularFilePath, RegularFileGuid}
          ]
      ),
  
      % Test setting primitive json values
  
      SetPrimitiveJsonDataSpec = #data_spec{
          required = [<<"metadata">>],
          correct_values = #{<<"metadata">> => [
              <<"{}">>, <<"[]">>, <<"true">>, <<"0">>, <<"0.1">>,
              <<"null">>, <<"\"string\"">>
          ]}
      },
  
      ConstructSetPrimitiveJsonVerifyEnvForSuccessfulCallsFun = fun(FileGuid) -> fun
          (false, #api_test_ctx{node = TestNode}) ->
              ?assertMatch({error, ?ENODATA}, GetMetadataFun(TestNode, FileGuid), ?ATTEMPTS),
              true;
          (true, #api_test_ctx{node = TestNode, data = #{<<"metadata">> := Metadata}}) ->
              % Primitive jsons are set without optional params so should match exactly to itself
              ExpMetadata = json_utils:decode(Metadata),
              lists:foreach(fun(Node) ->
                  ?assertMatch({ok, ExpMetadata}, GetMetadataFun(Node, FileGuid), ?ATTEMPTS)
              end, Providers),
  
              case Metadata of
                  <<"\"string\"">> ->
                      % Remove metadata after last successful parameters combination tested so that
                      % next tests can start from setting rather then updating metadata
                      ?assertMatch(ok, RemoveMetadataFun(TestNode, FileGuid)),
                      lists:foreach(fun(Node) ->
                          ?assertMatch({error, ?ENODATA}, GetMetadataFun(Node, FileGuid), ?ATTEMPTS)
                      end, Providers);
                  _ ->
                      ok
              end,
              true
      end end,
  
      set_metadata_test_base(
          Config,
          <<"json">>,
  
          GetMetadataFun,
          RemoveMetadataFun,
          fun(_) -> ok end,
          ConstructSetPrimitiveJsonVerifyEnvForSuccessfulCallsFun,
  
          SetPrimitiveJsonDataSpec,
          ShareId,
          [
              {<<"dir">>, DirPath, DirGuid},
              {<<"file">>, RegularFilePath, RegularFileGuid}
          ]
      ).
  
  
  -spec set_metadata_test_base(
      Config :: proplists:proplist(),
      MetadataType :: binary(),  %% <<"json">> | <<"rdf">> | <<"xattrs">>
  
      GetMetadataFun :: fun((node(), file_id:file_guid()) -> FileMetadata :: term()),
      RemoveMetadataFun :: fun((node(), file_id:file_guid()) -> ok),
      GetExpectedResultFun :: fun((api_test_ctx()) -> ok | {error, term()}),
      ConstructVerifyEnvForSuccessfulCallsFun :: fun((file_id:file_guid()) ->
          fun((ShouldSucceed :: boolean(), api_test_env()) -> boolean())
      ),
  
      DataSpec :: data_spec(),
      ShareId :: od_share:id(),
      FilesInSpace2List :: [{
          FileType :: binary(), % <<"file">> | <<"dir">>
          FilePath :: binary(),
          FileGuid :: file_id:file_guid()
      }]
  ) ->
      ok | no_return().
  set_metadata_test_base(
      Config,
      MetadataType,
  
      GetMetadataFun,
      RemoveMetadataFun,
      GetExpectedResultFun,
      ConstructVerifyEnvForSuccessfulCallsFun,
  
      DataSpec,
      ShareId,
      FilesInSpace2List
  ) ->
      [Provider2, Provider1] = Providers = ?config(op_worker_nodes, Config),
  
      SupportedClientsPerNode = #{
          Provider1 => [?USER_IN_SPACE_1_AUTH, ?USER_IN_SPACE_2_AUTH, ?USER_IN_BOTH_SPACES_AUTH],
          Provider2 => [?USER_IN_SPACE_2_AUTH, ?USER_IN_BOTH_SPACES_AUTH]
      },
  
      ClientSpecForGetJsonInSpace2Scenarios = #client_spec{
          correct = [?USER_IN_SPACE_2_AUTH, ?USER_IN_BOTH_SPACES_AUTH],
          unauthorized = [?NOBODY],
          forbidden = [?USER_IN_SPACE_1_AUTH],
          supported_clients_per_node = SupportedClientsPerNode
      },
  
      % Special case -> any user can make requests for shares but if request is
      % being made using credentials by user not supported on specific provider
      % ?ERROR_USER_NOT_SUPPORTED will be returned
      ClientSpecForShareScenarios = #client_spec{
          correct = [?NOBODY, ?USER_IN_SPACE_1_AUTH, ?USER_IN_SPACE_2_AUTH, ?USER_IN_BOTH_SPACES_AUTH],
          unauthorized = [],
          forbidden = [],
          supported_clients_per_node = SupportedClientsPerNode
      },
  
      QsParameters = case DataSpec of
          undefined ->
              [];
          #data_spec{optional = OptionalParams} ->
              OptionalParams
      end,
  
      ConstructPrepareRestArgsFun = fun(FileId) -> fun(#api_test_ctx{data = Data}) ->
          #rest_args{
              method = put,
              headers = case MetadataType of
                  <<"rdf">> -> #{<<"content-type">> => <<"application/rdf+xml">>};
                  _ -> #{<<"content-type">> => <<"application/json">>}
              end,
              path = http_utils:append_url_parameters(
                  <<"data/", FileId/binary, "/metadata/", MetadataType/binary>>,
                  maps:with(QsParameters, utils:ensure_defined(Data, undefined, #{}))
              ),
              body = encode_metadata(maps:get(<<"metadata">>, Data))
          }
      end end,
      ConstructPrepareDeprecatedFilePathRestArgsFun = fun(FilePath) ->
          fun(#api_test_ctx{data = Data}) ->
              #rest_args{
                  method = put,
                  headers = case MetadataType of
                      <<"rdf">> -> #{<<"content-type">> => <<"application/rdf+xml">>};
                      _ -> #{<<"content-type">> => <<"application/json">>}
                  end,
                  path = http_utils:append_url_parameters(
                      <<"metadata/", MetadataType/binary, FilePath/binary>>,
                      maps:with(QsParameters, utils:ensure_defined(Data, undefined, #{}))
                  ),
                  body = encode_metadata(maps:get(<<"metadata">>, Data))
              }
          end
      end,
      ConstructPrepareDeprecatedFileIdRestArgsFun = fun(Fileid) ->
          fun(#api_test_ctx{data = Data}) ->
              #rest_args{
                  method = put,
                  headers = case MetadataType of
                      <<"rdf">> -> #{<<"content-type">> => <<"application/rdf+xml">>};
                      _ -> #{<<"content-type">> => <<"application/json">>}
                  end,
                  path = http_utils:append_url_parameters(
                      <<"metadata-id/", MetadataType/binary, "/", Fileid/binary>>,
                      maps:with(QsParameters, utils:ensure_defined(Data, undefined, #{}))
                  ),
                  body = encode_metadata(maps:get(<<"metadata">>, Data))
              }
          end
      end,
      ConstructPrepareGsArgsFun = fun(FileId, Scope) -> fun(#api_test_ctx{data = Data0}) ->
          {Aspect, Data1} = case MetadataType of
              <<"json">> ->
                  % Primitive metadata were specified as binaries to be send via REST,
                  % but gs needs them decoded first to be able to send them properly
                  Meta = maps:get(<<"metadata">>, Data0),
                  {json_metadata, Data0#{<<"metadata">> => maybe_decode_json(Meta)}};
              <<"rdf">> ->
                  {rdf_metadata, Data0};
              <<"xattrs">> ->
                  {xattrs, Data0}
          end,
          #gs_args{
              operation = create,
              gri = #gri{type = op_file, id = FileId, aspect = Aspect, scope = Scope},
              data = Data1
          }
      end end,
  
      ValidateRestSuccessfulCallFun =  fun(TestCtx, {ok, RespCode, RespBody}) ->
          {ExpCode, ExpBody} = case GetExpectedResultFun(TestCtx) of
              ok ->
                  {?HTTP_204_NO_CONTENT, #{}};
              {error, _} = ExpError ->
                  {errors:to_http_code(ExpError), ?REST_ERROR(ExpError)}
          end,
          ?assertEqual({ExpCode, ExpBody}, {RespCode, RespBody})
      end,
      ValidateRestOperationNotSupportedFun = fun(_, {ok, ?HTTP_400_BAD_REQUEST, Response}) ->
          ?assertEqual(?REST_ERROR(?ERROR_NOT_SUPPORTED), Response)
      end,
  
      lists:foreach(fun({FileType, FilePath, FileGuid}) ->
          {ok, FileObjectId} = file_id:guid_to_objectid(FileGuid),
  
          ShareGuid = file_id:guid_to_share_guid(FileGuid, ShareId),
          {ok, ShareObjectId} = file_id:guid_to_objectid(ShareGuid),
  
          %% TEST SETTING METADATA FOR FILE IN NORMAL MODE
  
          ?assert(api_test_utils:run_tests(Config, [
  
              #scenario_spec{
                  name = <<"Set ", MetadataType/binary, " metadata for ", FileType/binary, " using /data/ rest endpoint">>,
                  type = rest,
                  target_nodes = Providers,
                  client_spec = ClientSpecForGetJsonInSpace2Scenarios,
                  prepare_args_fun = ConstructPrepareRestArgsFun(FileObjectId),
                  validate_result_fun = ValidateRestSuccessfulCallFun,
                  verify_fun = ConstructVerifyEnvForSuccessfulCallsFun(FileGuid),
                  data_spec = DataSpec
              },
              #scenario_spec{
                  name = <<"Set ", MetadataType/binary, " metadata for ", FileType/binary, " using /files/ rest endpoint">>,
                  type = rest_with_file_path,
                  target_nodes = Providers,
                  client_spec = ClientSpecForGetJsonInSpace2Scenarios,
                  prepare_args_fun = ConstructPrepareDeprecatedFilePathRestArgsFun(FilePath),
                  validate_result_fun = ValidateRestSuccessfulCallFun,
                  verify_fun = ConstructVerifyEnvForSuccessfulCallsFun(FileGuid),
                  data_spec = DataSpec
              },
              #scenario_spec{
                  name = <<"Set ", MetadataType/binary, " metadata for ", FileType/binary, " using /files-id/ rest endpoint">>,
                  type = rest,
                  target_nodes = Providers,
                  client_spec = ClientSpecForGetJsonInSpace2Scenarios,
                  prepare_args_fun = ConstructPrepareDeprecatedFileIdRestArgsFun(FileObjectId),
                  validate_result_fun = ValidateRestSuccessfulCallFun,
                  verify_fun = ConstructVerifyEnvForSuccessfulCallsFun(FileGuid),
                  data_spec = DataSpec
              },
              #scenario_spec{
                  name = <<"Set ", MetadataType/binary, " metadata for ", FileType/binary, " using gs api">>,
                  type = gs,
                  target_nodes = Providers,
                  client_spec = ClientSpecForGetJsonInSpace2Scenarios,
                  prepare_args_fun = ConstructPrepareGsArgsFun(FileGuid, private),
                  validate_result_fun = fun(TestCtx, Result) ->
                      case GetExpectedResultFun(TestCtx) of
                          ok ->
                              ?assertEqual({ok, undefined}, Result);
                          {error, _} = ExpError ->
                              ?assertEqual(ExpError, Result)
                      end
                  end,
                  verify_fun = ConstructVerifyEnvForSuccessfulCallsFun(FileGuid),
                  data_spec = DataSpec
              }
          ])),
  
          %% TEST SETTING METADATA FOR SHARED FILE SHOULD BE FORBIDDEN
  
          % Remove metadata and assert that no below call sets it again
          lists:foreach(fun(Node) ->
              ?assertMatch(ok, RemoveMetadataFun(Node, FileGuid))
          end, Providers),
  
          VerifyEnvForShareCallsFun = fun(_, #api_test_ctx{node = Node}) ->
              ?assertMatch({error, ?ENODATA}, GetMetadataFun(Node, FileGuid)),
              true
          end,
  
          ?assert(api_test_utils:run_tests(Config, [
  
              #scenario_spec{
                  name = <<"Set ", MetadataType/binary, " metadata for shared ", FileType/binary, " using /data/ rest endpoint">>,
                  type = rest_not_supported,
                  target_nodes = Providers,
                  client_spec = ClientSpecForShareScenarios,
                  prepare_args_fun = ConstructPrepareRestArgsFun(ShareObjectId),
                  validate_result_fun = ValidateRestOperationNotSupportedFun,
                  verify_fun = VerifyEnvForShareCallsFun,
                  data_spec = DataSpec
              },
              #scenario_spec{
                  name = <<"Set ", MetadataType/binary, " metadata for shared ", FileType/binary, " using /files-id/ rest endpoint">>,
                  type = rest_not_supported,
                  target_nodes = Providers,
                  client_spec = ClientSpecForShareScenarios,
                  prepare_args_fun = ConstructPrepareDeprecatedFileIdRestArgsFun(ShareObjectId),
                  validate_result_fun = ValidateRestOperationNotSupportedFun,
                  verify_fun = VerifyEnvForShareCallsFun,
                  data_spec = DataSpec
              },
              #scenario_spec{
                  name = <<"Set ", MetadataType/binary, " metadata for shared ", FileType/binary, " using gs public api">>,
                  type = gs_not_supported,
                  target_nodes = Providers,
                  client_spec = ClientSpecForShareScenarios,
                  prepare_args_fun = ConstructPrepareGsArgsFun(ShareGuid, public),
                  validate_result_fun = fun(_TestCaseCtx, Result) ->
                      ?assertEqual(?ERROR_NOT_SUPPORTED, Result)
                  end,
                  verify_fun = VerifyEnvForShareCallsFun,
                  data_spec = DataSpec
              },
              #scenario_spec{
                  name = <<"Set ", MetadataType/binary, " metadata for shared ", FileType/binary, " using private gs api">>,
                  type = gs,
                  target_nodes = Providers,
                  client_spec = ClientSpecForShareScenarios,
                  prepare_args_fun = ConstructPrepareGsArgsFun(ShareGuid, private),
                  validate_result_fun = fun(_, Result) ->
                      ?assertEqual(?ERROR_UNAUTHORIZED, Result)
                  end,
                  verify_fun = VerifyEnvForShareCallsFun,
                  data_spec = DataSpec
              }
          ]))
      end, FilesInSpace2List),
  
      %% TEST SET METADATA FOR FILE ON PROVIDER NOT SUPPORTING USER
  
      Space1Guid = fslogic_uuid:spaceid_to_space_dir_guid(?SPACE_1),
      {ok, Space1ObjectId} = file_id:guid_to_objectid(Space1Guid),
  
      % Remove metadata on space1 that may have been set by previous tests
      lists:foreach(fun(Node) -> ?assertMatch(ok, RemoveMetadataFun(Node, Space1Guid)) end, Providers),
  
      Provider2DomainBin = ?GET_DOMAIN_BIN(Provider2),
  
      % For testing setting metadata on provider not supporting user (expected error)
      % only bare minimum to make call is needed. That is why optional parameters
      % are erased (they were tested already in above scenarios)
      DataSpecForSetMetadataInSpace1Scenarios = DataSpec#data_spec{optional = []},
  
      ClientSpecForSetMetadataInSpace1Scenarios = #client_spec{
          correct = [?USER_IN_SPACE_1_AUTH, ?USER_IN_BOTH_SPACES_AUTH],
          unauthorized = [?NOBODY],
          forbidden = [?USER_IN_SPACE_2_AUTH],
          supported_clients_per_node = SupportedClientsPerNode
      },
  
      ValidateRestSetMetadataOnProvidersNotSupportingUserFun = fun
          (#api_test_ctx{node = Node, client = Client}, {ok, ?HTTP_400_BAD_REQUEST, Response}) when
              Node == Provider2,
              Client == ?USER_IN_BOTH_SPACES_AUTH
          ->
              ?assertEqual(?REST_ERROR(?ERROR_SPACE_NOT_SUPPORTED_BY(Provider2DomainBin)), Response);
          (_TestCaseCtx, {ok, ?HTTP_204_NO_CONTENT, Response}) ->
              ?assertEqual(#{}, Response)
      end,
      ConstructVerifyEnvFunForSetMetadataInSpace1Scenarios = fun(FileGuid) -> fun
          (false, #api_test_ctx{node = Node}) ->
              ?assertMatch({error, ?ENODATA}, GetMetadataFun(Node, FileGuid), ?ATTEMPTS),
              true;
          (true, #api_test_ctx{node = Node, client = Client, data = #{<<"metadata">> := Metadata}}) ->
              case {Node, Client} of
                  {Provider2, ?USER_IN_BOTH_SPACES_AUTH} ->
                      % Request from user not supported by provider should be rejected
                      ?assertMatch({error, ?ENODATA}, GetMetadataFun(Node, FileGuid), ?ATTEMPTS);
                  _ ->
                      ExpMetadata = case MetadataType of
                          <<"json">> -> maybe_decode_json(Metadata);
                          _ -> Metadata
                      end,
                      ?assertMatch({ok, ExpMetadata}, GetMetadataFun(Node, FileGuid)),
                      ?assertMatch(ok, RemoveMetadataFun(Node, FileGuid))
              end,
              true
      end end,
  
      ?assert(api_test_utils:run_tests(Config, [
          #scenario_spec{
              name = <<"Set ", MetadataType/binary, " metadata for ", ?SPACE_1/binary, " on provider not supporting user using /data/ rest endpoint">>,
              type = rest,
              target_nodes = Providers,
              client_spec = ClientSpecForSetMetadataInSpace1Scenarios,
              prepare_args_fun = ConstructPrepareRestArgsFun(Space1ObjectId),
              validate_result_fun = ValidateRestSetMetadataOnProvidersNotSupportingUserFun,
              verify_fun = ConstructVerifyEnvFunForSetMetadataInSpace1Scenarios(Space1Guid),
              data_spec = DataSpecForSetMetadataInSpace1Scenarios
          },
          #scenario_spec{
              name = <<"Set ", MetadataType/binary, " metadata for ", ?SPACE_1/binary, " on provider not supporting user using /files/ rest endpoint">>,
              type = rest_with_file_path,
              target_nodes = Providers,
              client_spec = ClientSpecForSetMetadataInSpace1Scenarios,
              prepare_args_fun = ConstructPrepareDeprecatedFilePathRestArgsFun(<<"/", ?SPACE_1/binary>>),
              validate_result_fun = ValidateRestSetMetadataOnProvidersNotSupportingUserFun,
              verify_fun = ConstructVerifyEnvFunForSetMetadataInSpace1Scenarios(Space1Guid),
              data_spec = DataSpecForSetMetadataInSpace1Scenarios
          },
          #scenario_spec{
              name = <<"Set ", MetadataType/binary, " metadata for ", ?SPACE_1/binary, " on provider not supporting user using /files-id/ rest endpoint">>,
              type = rest,
              target_nodes = Providers,
              client_spec = ClientSpecForSetMetadataInSpace1Scenarios,
              prepare_args_fun = ConstructPrepareDeprecatedFileIdRestArgsFun(Space1ObjectId),
              validate_result_fun = ValidateRestSetMetadataOnProvidersNotSupportingUserFun,
              verify_fun = ConstructVerifyEnvFunForSetMetadataInSpace1Scenarios(Space1Guid),
              data_spec = DataSpecForSetMetadataInSpace1Scenarios
          },
          #scenario_spec{
              name = <<"Set ", MetadataType/binary, " metadata for ", ?SPACE_1/binary, " on provider not supporting user using gs api">>,
              type = gs,
              target_nodes = Providers,
              client_spec = ClientSpecForSetMetadataInSpace1Scenarios,
              prepare_args_fun = ConstructPrepareGsArgsFun(Space1Guid, private),
              validate_result_fun = fun
                  (#api_test_ctx{node = Node, client = Client}, Result) when
                      Node == Provider2,
                      Client == ?USER_IN_BOTH_SPACES_AUTH
                  ->
                      ?assertEqual(?ERROR_SPACE_NOT_SUPPORTED_BY(Provider2DomainBin), Result);
                  (_TestCaseCtx, Result) ->
                      ?assertEqual({ok, undefined}, Result)
              end,
              verify_fun = ConstructVerifyEnvFunForSetMetadataInSpace1Scenarios(Space1Guid),
              data_spec = DataSpecForSetMetadataInSpace1Scenarios
          }
      ])).
  
  
  %%%===================================================================
  %%% SetUp and TearDown functions
  %%%===================================================================
  
negative.code:     set_dir_rdf_metadata_on_provider_not_supporting_space_test/1,
  
      % Set json metadata test cases
      set_file_json_metadata_test/1,
      set_dir_json_metadata_test/1,
      set_file_primitive_json_metadata_test/1,
      set_dir_primitive_json_metadata_test/1,
      set_shared_file_json_metadata_test/1,
      set_shared_dir_json_metadata_test/1,
      set_file_json_metadata_on_provider_not_supporting_space_test/1,
      set_dir_json_metadata_on_provider_not_supporting_space_test/1
  ]).
  
  all() ->
  