/* * Copyright (C) 2018 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.copybara.git; import static com.google.common.truth.Truth.assertThat; import static com.google.copybara.testing.git.GitTestUtil.mockGitHubNotFound; import static com.google.copybara.testing.git.GitTestUtil.mockResponse; import static com.google.copybara.testing.git.GitTestUtil.mockResponseAndValidateRequest; import static com.google.copybara.testing.git.GitTestUtil.mockResponseWithStatus; import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.contains; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import com.google.api.client.json.gson.GsonFactory; import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.jimfs.Jimfs; import com.google.copybara.exception.ValidationException; import com.google.copybara.feedback.Feedback; import com.google.copybara.testing.DummyChecker; import com.google.copybara.testing.DummyTrigger; import com.google.copybara.testing.OptionsBuilder; import com.google.copybara.testing.SkylarkTestExecutor; import com.google.copybara.testing.git.GitTestUtil; import com.google.copybara.testing.git.GitTestUtil.MockRequestAssertion; import com.google.copybara.util.console.Message.MessageType; import com.google.copybara.util.console.testing.TestingConsole; import com.google.devtools.build.lib.syntax.Starlark; import java.io.IOException; import java.io.Serializable; import java.nio.file.Files; import java.nio.file.Path; import java.util.Iterator; import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @RunWith(JUnit4.class) public class GitHubEndpointTest { private SkylarkTestExecutor skylark; private TestingConsole console; private DummyTrigger dummyTrigger; private Path workdir; private GitTestUtil gitUtil; @Before public void setup() throws Exception { workdir = Jimfs.newFileSystem().getPath("/"); console = new TestingConsole(); OptionsBuilder options = new OptionsBuilder(); options.setConsole(console) .setOutputRootToTmpDir(); gitUtil = new GitTestUtil(options); gitUtil.mockRemoteGitRepos(); dummyTrigger = new DummyTrigger(); options.testingOptions.feedbackTrigger = dummyTrigger; options.testingOptions.checker = new DummyChecker(ImmutableSet.of("badword")); gitUtil.mockApi(eq("GET"), contains("master/status"), mockResponse("{\n" + " state : 'failure',\n" + " total_count : 2,\n" + " sha : 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',\n" + " statuses : [\n" + " { state : 'failure', context: 'some/context'},\n" + " { state : 'success', context: 'other/context'}\n" + " ]\n" + "}")); gitUtil.mockApi(eq("GET"), contains("/commits/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), mockResponse("{\n" + " sha : 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',\n" + " commit : {\n" + " author: { name : 'theauthor', email: '[email protected]'},\n" + " committer: { name : 'thecommitter', email: '[email protected]'},\n" + " message: \"This is a message\\n\\nWith body\\n\"\n" + " },\n" + " committer : { login : 'github_committer'},\n" + " author : { login : 'github_author'}\n" + "}")); gitUtil.mockApi(eq("POST"), contains("/status"), mockResponse("{\n" + " state : 'success',\n" + " target_url : 'https://github.com/google/example',\n" + " description : 'Observed foo',\n" + " context : 'test'\n" + "}")); gitUtil.mockApi( anyString(), contains("/git/refs/heads/test"), mockResponse( "{\n" + " ref : 'refs/heads/test',\n" + " url : 'https://github.com/google/example/git/refs/heads/test',\n" + " object : { \n" + " type : 'commit',\n" + " sha : 'e597746de9c1704e648ddc3ffa0d2096b146d600', \n" + " url :" + " 'https://github.com/google/example/git/commits/e597746de9c1704e648ddc3ffa0d2096b146d600'\n" + " } \n" + "}")); gitUtil.mockApi( eq("GET"), contains("git/refs?per_page=100"), mockResponse( "[{\n" + " ref : 'refs/heads/test',\n" + " url : 'https://github.com/google/example/git/refs/heads/test',\n" + " object : { \n" + " type : 'commit',\n" + " sha : 'e597746de9c1704e648ddc3ffa0d2096b146d600', \n" + " url :" + " 'https://github.com/google/example/git/commits/e597746de9c1704e648ddc3ffa0d2096b146d600'\n" + " } \n" + "}]")); gitUtil.mockApi( eq("GET"), contains("commits/e597746de9c1704e648ddc3ffa0d2096b146d610/check-runs"), mockResponse( "{\n" + " total_count: 1,\n" + " check_runs: [\n" + " {\n" + " id: 4,\n" + " details_url: 'https://example.com',\n" + " status: 'completed',\n" + " conclusion: 'neutral',\n" + " name: 'mighty_readme',\n" + " output: {\n" + " title: 'Mighty Readme report',\n" + " summary: 'test_summary',\n" + " text: 'test_text'\n" + " },\n" + " app: {\n" + " id: 1,\n" + " slug: 'octoapp',\n" + " name: 'Octocat App'\n" + " }\n" + " }\n" + " ]\n" + "}" )); Path credentialsFile = Files.createTempFile("credentials", "test"); Files.write(credentialsFile, "https://user:[email protected]".getBytes(UTF_8)); options.git.credentialHelperStorePath = credentialsFile.toString(); skylark = new SkylarkTestExecutor(options); } @Test public void testParsing() throws Exception { skylark.eval( "e", "e = git.github_api(url = 'https://github.com/google/example')"); } @Test public void testParsingWithChecker() throws Exception { skylark.eval( "e", "e = git.github_api(\n" + "url = 'https://github.com/google/example', \n" + "checker = testing.dummy_checker(),\n" + ")\n"); } @Test public void testCheckerIsHonored() throws Exception { String config = "" + "def test_action(ctx):\n" + " ctx.destination.update_reference(\n" + " 'e597746de9c1704e648ddc3ffa0d2096b146d600', 'foo_badword_bar', True)\n" + " return ctx.success()\n" + "\n" + "core.feedback(\n" + " name = 'default',\n" + " origin = testing.dummy_trigger(),\n" + " destination = git.github_api(" + " url = 'https://github.com/google/example',\n" + " checker = testing.dummy_checker(),\n" + " ),\n" + " actions = [test_action,],\n" + ")\n" + "\n"; Feedback feedback = (Feedback) skylark.loadConfig(config).getMigration("default"); assertThat(feedback.getDestinationDescription().get("url")) .containsExactly("https://github.com/google/example"); ValidationException expected = assertThrows( ValidationException.class, () -> feedback.run(workdir, ImmutableList.of("12345"))); assertThat(expected) .hasMessageThat() .contains("Bad word 'badword' found: field 'path'. Location: copy.bara.sky:2:3"); } @Test public void testParsingEmptyUrl() { skylark.evalFails("git.github_api(url = '')", "Invalid empty field 'url'"); } @Test public void testOriginRef() throws Exception { runFeedback(ImmutableList.<String>builder() .add("res = ctx.destination.new_origin_ref('12345')") .addAll(checkFieldStarLark("res", "ref", "'12345'")) .build()); } /** * A test that uses feedback. * * <p>Does not verify all the fields, see {@link #testCreateStatusExhaustive()} for that. */ @Test public void testFeedbackCreateStatus() throws Exception{ dummyTrigger.addAll("Foo", "Bar"); Feedback feedback = feedback( "" + "def test_action(ctx):\n" + " ref = 'None'\n" + " if len(ctx.refs) > 0:\n" + " ref = ctx.refs[0]\n" + " \n" + " for m in ctx.origin.get_messages:\n" + " status = ctx.destination.create_status(\n" + " sha = ref,\n" + " state = 'success',\n" + " context = 'test',\n" + " description = 'Observed ' + m,\n" + " )\n" + " ctx.console.info('Created status')\n" + " return ctx.success()\n" + "\n"); Iterator<String> createValues = ImmutableList.of("Observed Foo", "Observed Bar").iterator(); gitUtil.mockApi(eq("POST"), contains("/status"), mockResponseAndValidateRequest("{\n" + " state : 'success',\n" + " target_url : 'https://github.com/google/example',\n" + " description : 'Observed foo',\n" + " context : 'test'\n" + "}", new MockRequestAssertion(String.format( "Requests were expected to cycle through the values of %s", createValues), r -> r.contains(createValues.next())))); feedback.run(workdir, ImmutableList.of("e597746de9c1704e648ddc3ffa0d2096b146d600")); console.assertThat().timesInLog(2, MessageType.INFO, "Created status"); verify(gitUtil.httpTransport(), times(2)).buildRequest(eq("POST"), contains("/status")); } @Test public void testCreateStatusExhaustive() throws Exception { runFeedback(ImmutableList.<String>builder() .add("res = ctx.destination.create_status(sha = 'e597746de9c1704e648ddc3ffa0d2096b146d600'," + " state = 'success', context = 'test', description = 'Observed foo')") .addAll(checkFieldStarLark("res", "state", "'success'")) .addAll(checkFieldStarLark("res", "target_url", "'https://github.com/google/example'")) .addAll(checkFieldStarLark("res", "description", "'Observed foo'")) .addAll(checkFieldStarLark("res", "context", "'test'")) .build()); } @Test public void testGetCombinedStatus() throws Exception { runFeedback(ImmutableList.<String>builder() .add("res = ctx.destination.get_combined_status(ref = 'master')") .addAll(checkFieldStarLark("res", "state", "'failure'")) .addAll(checkFieldStarLark("res", "total_count", "2")) .addAll(checkFieldStarLark("res", "statuses[0].context", "'some/context'")) .addAll(checkFieldStarLark("res", "statuses[0].state", "'failure'")) .addAll(checkFieldStarLark("res", "statuses[1].context", "'other/context'")) .addAll(checkFieldStarLark("res", "statuses[1].state", "'success'")) .build()); } @Test public void testGetCheckRuns() throws Exception { runFeedback(ImmutableList.<String>builder() .add("res = ctx.destination.get_check_runs(sha='e597746de9c1704e648ddc3ffa0d2096b146d610')") .addAll(checkFieldStarLark("res", "total_count", "1")) .addAll(checkFieldStarLark("res", "check_runs[0].detail_url", "'https://example.com'")) .addAll(checkFieldStarLark("res", "check_runs[0].status", "'completed'")) .addAll(checkFieldStarLark("res", "check_runs[0].conclusion", "'neutral'")) .addAll(checkFieldStarLark("res", "check_runs[0].name", "'mighty_readme'")) .addAll(checkFieldStarLark("res", "check_runs[0].app.id", "1")) .addAll(checkFieldStarLark("res", "check_runs[0].app.slug", "'octoapp'")) .addAll(checkFieldStarLark("res", "check_runs[0].app.name", "'Octocat App'")) .addAll(checkFieldStarLark("res", "check_runs[0].output.title", "'Mighty Readme report'")) .addAll(checkFieldStarLark("res", "check_runs[0].output.summary", "'test_summary'")) .addAll(checkFieldStarLark("res", "check_runs[0].output.text", "'test_text'")) .build()); } @Test public void testGetCombinedStatus_notFound() throws Exception { gitUtil.mockApi( eq("GET"), eq("https://api.github.com/repos/google/example/commits/heads/not_found/status"), mockGitHubNotFound()); runFeedback(ImmutableList.<String>builder() .add("res = {}") .add("res['foo'] = ctx.destination.get_combined_status(ref = 'heads/not_found')") .addAll(checkFieldStarLark("res", "get('foo')", "None")) .build()); } @Test public void testGetPullRequestComment() throws Exception { gitUtil.mockApi( eq("GET"), eq("https://api.github.com/repos/google/example/pulls/comments/12345"), mockResponse(toJson(jsonComment()))); runFeedback(ImmutableList.<String>builder() .add("res = ctx.destination.get_pull_request_comment(comment_id = '12345')") .addAll(checkFieldStarLark("res", "id", "'12345'")) .addAll(checkFieldStarLark("res", "path", "'foo/Bar.java'")) .addAll(checkFieldStarLark("res", "body", "'This needs to be fixed.'")) .addAll(checkFieldStarLark("res", "diff_hunk", "'@@ -36,11 +35,16 @@ foo bar'")) .build()); } private static ImmutableMap<String, ? extends Serializable> jsonComment() { return ImmutableMap.of( "id", 12345, "path", "foo/Bar.java", "body", "This needs to be fixed.", "diff_hunk", "@@ -36,11 +35,16 @@ foo bar"); } @Test public void testGetPullRequestComment_notFound() { gitUtil.mockApi( eq("GET"), eq("https://api.github.com/repos/google/example/pulls/comments/12345"), mockGitHubNotFound()); ValidationException expected = assertThrows(ValidationException.class, () -> runFeedback( ImmutableList.of("ctx.destination.get_pull_request_comment(comment_id = '12345')"))); assertThat(expected).hasMessageThat().contains("Pull Request Comment not found"); } @Test public void testGetPullRequestComment_invalidId() { ValidationException expected = assertThrows(ValidationException.class, () -> runFeedback( ImmutableList.of("ctx.destination.get_pull_request_comment(comment_id = 'foo')"))); assertThat(expected).hasMessageThat().contains("Invalid comment id foo"); } @Test public void testGetPullRequestComments() throws Exception { gitUtil.mockApi( eq("GET"), eq("https://api.github.com/repos/google/example/pulls/12345/comments?per_page=100"), mockResponse(toJson(ImmutableList.of(jsonComment(), jsonComment())))); runFeedback(ImmutableList.<String>builder() .add("res = ctx.destination.get_pull_request_comments(number = 12345)") .addAll(checkFieldStarLark("res[0]", "id", "'12345'")) .addAll(checkFieldStarLark("res[0]", "path", "'foo/Bar.java'")) .addAll(checkFieldStarLark("res[0]", "body", "'This needs to be fixed.'")) .addAll(checkFieldStarLark("res[0]", "diff_hunk", "'@@ -36,11 +35,16 @@ foo bar'")) .addAll(checkFieldStarLark("res[1]", "id", "'12345'")) .build()); } @Test public void testGetPullRequestComments_notFound() { gitUtil.mockApi( eq("GET"), eq("https://api.github.com/repos/google/example/pulls/12345/comments?per_page=100"), mockGitHubNotFound()); ValidationException expected = assertThrows(ValidationException.class, () -> runFeedback(ImmutableList.of("ctx.destination.get_pull_request_comments(number = 12345)"))); assertThat(expected).hasMessageThat().contains("Pull Request Comments not found"); } @Test public void testGetCommit() throws Exception { runFeedback(ImmutableList.<String>builder() .add("res = ctx.destination.get_commit(ref = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa')") .addAll(checkFieldStarLark("res", "sha", "'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'")) .addAll(checkFieldStarLark("res", "commit.author.name", "'theauthor'")) .addAll(checkFieldStarLark("res", "commit.author.email", "'[email protected]'")) .addAll(checkFieldStarLark("res", "commit.committer.name", "'thecommitter'")) .addAll(checkFieldStarLark("res", "commit.committer.email", "'[email protected]'")) .addAll(checkFieldStarLark("res", "commit.message", "'This is a message\\n\\nWith body\\n'")) .addAll(checkFieldStarLark("res", "author.login", "'github_author'")) .addAll(checkFieldStarLark("res", "committer.login", "'github_committer'")) .build()); } @Test public void testGetCommitNotFound() throws Exception { gitUtil.mockApi(eq("GET"), eq("https://api.github.com/repos/google/example/commits/" + "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"), mockGitHubNotFound()); runFeedback(ImmutableList.<String>builder() .add("res = {}") .add("res['foo'] = ctx.destination.get_commit(" + "ref = 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb')") .addAll(checkFieldStarLark("res", "get('foo')", "None")) .build()); } @Test public void testGetReferenceNotFound() throws Exception { gitUtil.mockApi(eq("GET"), eq("https://api.github.com/repos/google/example/git/refs/heads/not_found"), mockGitHubNotFound()); runFeedback(ImmutableList.<String>builder() .add("res = {}") .add("res['foo'] = ctx.destination.get_reference(ref = 'refs/heads/not_found')") .addAll(checkFieldStarLark("res", "get('foo')", "None")) .build()); } /** * A test that uses update_reference. * */ @Test public void testFeedbackUpdateReference() throws Exception{ runFeedback(ImmutableList.<String>builder() .add("res = ctx.destination.update_reference('e597746de9c1704e648ddc3ffa0d2096b146d600'," + "'refs/heads/test', True)") .addAll(checkFieldStarLark("res", "ref", "'refs/heads/test'")) .addAll(checkFieldStarLark("res", "url", "'https://github.com/google/example/git/refs/heads/test'")) .addAll(checkFieldStarLark("res", "sha", "'e597746de9c1704e648ddc3ffa0d2096b146d600'")) .build()); } @Test public void testFeedbackUpdateReferenceShortRef() throws Exception{ runFeedback(ImmutableList.<String>builder() .add("res = ctx.destination.update_reference('e597746de9c1704e648ddc3ffa0d2096b146d600'," + " 'test', True)") .addAll(checkFieldStarLark("res", "ref", "'refs/heads/test'")) .addAll(checkFieldStarLark("res", "url", "'https://github.com/google/example/git/refs/heads/test'")) .addAll(checkFieldStarLark("res", "sha", "'e597746de9c1704e648ddc3ffa0d2096b146d600'")) .build()); } @Test public void testFeedbackDeleteReference() throws Exception{ AtomicBoolean called = new AtomicBoolean(false); gitUtil.mockApi(eq("DELETE"), contains("/git/refs/heads/test"), mockResponseWithStatus("", 202, new MockRequestAssertion("Always true with side-effect", s -> { called.set(true); return true; }))); runFeedback(ImmutableList.of("ctx.destination.delete_reference('refs/heads/test')")); assertThat(called.get()).isTrue(); } @Test public void testFeedbackDeleteReference_MasterCheck() { AtomicBoolean called = new AtomicBoolean(false); gitUtil.mockApi(eq("DELETE"), contains("/git/refs/heads/master"), mockResponseWithStatus("", 202, new MockRequestAssertion("Always true with side-effect", s -> { called.set(true); return true; }))); ValidationException expected = assertThrows(ValidationException.class, () -> runFeedback(ImmutableList.of("ctx.destination.delete_reference('refs/heads/master')"))); assertThat(expected).hasMessageThat().contains("Copybara doesn't allow to delete master"); assertThat(called.get()).isFalse(); } /** * A test that uses get_reference. * */ @Test public void testGetReference() throws Exception{ runFeedback(ImmutableList.<String>builder() .add("res = ctx.destination.get_reference('refs/heads/test')") .addAll(checkFieldStarLark("res", "ref", "'refs/heads/test'")) .addAll(checkFieldStarLark("res", "url", "'https://github.com/google/example/git/refs/heads/test'")) .addAll(checkFieldStarLark("res", "sha", "'e597746de9c1704e648ddc3ffa0d2096b146d600'")) .build()); } /** * A test that uses get_references. * */ @Test public void testGetReferences() throws Exception{ runFeedback(ImmutableList.<String>builder() .add("res = ctx.destination.get_references()") .addAll(checkFieldStarLark("res[0]", "ref", "'refs/heads/test'")) .addAll(checkFieldStarLark("res[0]", "url", "'https://github.com/google/example/git/refs/heads/test'")) .addAll(checkFieldStarLark("res[0]", "sha", "'e597746de9c1704e648ddc3ffa0d2096b146d600'")) .build()); } /** * A test that uses get_pull_requests. */ @Test public void testPullRequests() throws Exception { gitUtil.mockApi(anyString(), contains( "repos/google/example/pulls?per_page=100&state=open&sort=created&direction=asc"), mockResponse(toJson( ImmutableList.of( ImmutableMap.of( "number", 12345, "state", "open", "head", ImmutableMap.of( "label", "someuser:somebranch", "sha", Strings.repeat("a", 40), "ref", "somebranch" )))))); runFeedback(ImmutableList.<String>builder() .add("res = ctx.destination.get_pull_requests(state='OPEN')") .addAll(checkFieldStarLark("res[0]", "number", "12345")) .addAll(checkFieldStarLark("res[0]", "state", "'OPEN'")) .addAll(checkFieldStarLark("res[0]", "head.label", "'someuser:somebranch'")) .addAll(checkFieldStarLark("res[0]", "head.sha", "'" + Strings.repeat("a", 40) + "'")) .addAll(checkFieldStarLark("res[0]", "head.ref", "'somebranch'")) .build()); } @Test public void testUpdatePullRequest() throws Exception { gitUtil.mockApi(eq("POST"), contains("repos/google/example/pulls/12345"), mockResponseAndValidateRequest(toJson( ImmutableMap.of( "number", 12345, "state", "closed", "head", ImmutableMap.of( "label", "someuser:somebranch", "sha", Strings.repeat("a", 40), "ref", "somebranch" ))), MockRequestAssertion.contains("{\"state\":\"closed\"}"))); runFeedback(ImmutableList.<String>builder() .add("res = ctx.destination.update_pull_request(12345, state='CLOSED')") .addAll(checkFieldStarLark("res", "number", "12345")) .addAll(checkFieldStarLark("res", "state", "'CLOSED'")) .addAll(checkFieldStarLark("res", "head.label", "'someuser:somebranch'")) .addAll(checkFieldStarLark("res", "head.sha", "'" + Strings.repeat("a", 40) + "'")) .addAll(checkFieldStarLark("res", "head.ref", "'somebranch'")) .build()); } @Test public void testGetAuthenticatedUser() throws Exception { gitUtil.mockApi(eq("GET"), contains("user"), mockResponse(toJson(ImmutableMap.of("login", "tester")))); runFeedback(ImmutableList.<String>builder() .add("res = ctx.destination.get_authenticated_user()") .addAll(checkFieldStarLark("res", "login", "'tester'")) .build()); } /** * A test that uses get_pull_requests. */ @Test public void testPullRequests_badPrefix() throws Exception { ValidationException expected = assertThrows(ValidationException.class, () -> runFeedback(ImmutableList.of("ctx.destination.get_pull_requests(head_prefix = '[email protected]*')"))); assertThat(expected).hasMessageThat().contains("'[email protected]*' is not a valid head_prefix"); } @Test public void testAddlabel() throws Exception { gitUtil.mockApi( eq("POST"), contains("12345/labels"), mockResponse( "[\n" + " {\n" + " \"id\": 123456,\n" + " \"node_id\": \"BASE64=\",\n" + " \"url\": \"https://api.github.com/repos/google/example/labels/run_kokoro\",\n" + " \"name\": \"run_kokoro\",\n" + " \"description\": \"Run me!\",\n" + " \"color\": \"ffffff\",\n" + " \"default\": true\n" + " }" + "]" )); runFeedback(ImmutableList.<String>builder() .add("res = ctx.destination.add_label(number = 12345, labels = ['run_kokoro'])") .build()); verify(gitUtil.httpTransport()) .buildRequest(eq("POST"), contains("google/example/issues/12345/labels")); } private String toJson(Object obj) throws IOException { return GsonFactory.getDefaultInstance().toPrettyString(obj); } // var, field, and value are all Starlark expressions. private static ImmutableList<String> checkFieldStarLark(String var, String field, String value) { return ImmutableList.of( String.format("if %s.%s != %s:", var, field, value), String.format( " fail('unexpected value for '+%1$s+'.'+%2$s+' (expected '+%3$s+'): ' + %4$s.%5$s)", Starlark.repr(var), // string literal Starlark.repr(field), // string literal Starlark.repr(value), // string literal var, // expression field)); // expression } private void runFeedback(ImmutableList<String> funBody) throws Exception { Feedback test = feedback("def test_action(ctx):\n" + funBody.stream().map(s -> " " + s).collect(Collectors.joining("\n")) + "\n return ctx.success()\n"); test.run(workdir, ImmutableList.of("e597746de9c1704e648ddc3ffa0d2096b146d600")); } private Feedback feedback(String actionFunction) throws IOException, ValidationException { String config = actionFunction + "\n" + "core.feedback(\n" + " name = 'default',\n" + " origin = testing.dummy_trigger(),\n" + " destination = git.github_api(\n" + " url = 'https://github.com/google/example',\n" + " ),\n" + " actions = [test_action,],\n" + ")\n" + "\n"; System.err.println(config); return (Feedback) skylark.loadConfig(config).getMigration("default"); } }