From 2010c2a60aca91916493111d100e844e479f62bf Mon Sep 17 00:00:00 2001 From: William Cheng Date: Mon, 5 May 2025 15:32:05 +0800 Subject: [PATCH] Add workflow to test Elixir clients (#21214) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add elixir workflow * update * fix * add elixir workflow (#21215) * update tests to use built-in json module instead of jason * update base_url * temporarily disable type-casting for dates * retry failing tests * update spec to use localhost * add petsore local server to workflow --------- Co-authored-by: Enrique Fernández --- .github/workflows/samples-elixir.yaml | 39 +++++++++++++++++ ...ith-fake-endpoints-models-for-testing.yaml | 3 ++ .../petstore/elixir/.openapi-generator-ignore | 1 + samples/client/petstore/elixir/README.md | 4 +- .../client/petstore/elixir/config/config.exs | 2 +- .../elixir/lib/openapi_petstore/connection.ex | 6 +-- .../elixir/test/deserializer_test.exs | 6 +-- .../petstore/elixir/test/format_test.exs | 6 +-- ...s_and_additional_properties_class_test.exs | 2 +- .../petstore/elixir/test/outer_enum_test.exs | 4 +- .../client/petstore/elixir/test/pet_test.exs | 42 +++++++++++++------ 11 files changed, 88 insertions(+), 27 deletions(-) create mode 100644 .github/workflows/samples-elixir.yaml diff --git a/.github/workflows/samples-elixir.yaml b/.github/workflows/samples-elixir.yaml new file mode 100644 index 00000000000..64b85951ae2 --- /dev/null +++ b/.github/workflows/samples-elixir.yaml @@ -0,0 +1,39 @@ +name: Samples Elixir + +on: + push: + paths: + - samples/client/petstore/elixir/** + pull_request: + paths: + - samples/client/petstore/elixir/** +jobs: + build: + runs-on: ubuntu-latest + name: OTP ${{matrix.otp}} / Elixir ${{matrix.elixir}} + strategy: + matrix: + otp: ['25.3.2', '26.2.5', '27.3.3'] + elixir: ['1.18.3'] + sample: + - samples/client/petstore/elixir/ + services: + petstore-api: + image: swaggerapi/petstore + ports: + - 80:8080 + env: + SWAGGER_HOST: http://petstore.swagger.io + SWAGGER_BASE_PATH: /v2 + steps: + - uses: actions/checkout@v4 + - uses: erlef/setup-beam@v1 + with: + otp-version: ${{matrix.otp}} + elixir-version: ${{matrix.elixir}} + - name: mix deps.get + run: mix deps.get + working-directory: ${{ matrix.sample }} + - name: mix test + run: mix test + working-directory: ${{ matrix.sample }} diff --git a/modules/openapi-generator/src/test/resources/3_0/elixir/petstore-with-fake-endpoints-models-for-testing.yaml b/modules/openapi-generator/src/test/resources/3_0/elixir/petstore-with-fake-endpoints-models-for-testing.yaml index b2dd68dfe06..33b40979364 100644 --- a/modules/openapi-generator/src/test/resources/3_0/elixir/petstore-with-fake-endpoints-models-for-testing.yaml +++ b/modules/openapi-generator/src/test/resources/3_0/elixir/petstore-with-fake-endpoints-models-for-testing.yaml @@ -31,6 +31,7 @@ paths: $ref: "#/components/schemas/Foo" /pet: servers: + - url: "http://localhost/v2" - url: "http://petstore.swagger.io/v2" - url: "http://path-server-test.petstore.local/v2" - url: "http://{server}.swagger.io:{port}/v2" @@ -1361,6 +1362,8 @@ paths: schema: $ref: "#/components/schemas/AllOfWithSingleRef" servers: + - url: "http://localhost/v2" + - url: "https://petstore.swagger.io/v2" - url: "http://{server}.swagger.io:{port}/v2" description: petstore server variables: diff --git a/samples/client/petstore/elixir/.openapi-generator-ignore b/samples/client/petstore/elixir/.openapi-generator-ignore index 7484ee590a3..daed634bb4b 100644 --- a/samples/client/petstore/elixir/.openapi-generator-ignore +++ b/samples/client/petstore/elixir/.openapi-generator-ignore @@ -21,3 +21,4 @@ #docs/*.md # Then explicitly reverse the ignore rule for a single file: #!docs/README.md +# diff --git a/samples/client/petstore/elixir/README.md b/samples/client/petstore/elixir/README.md index 898f669267d..ae80db6ad42 100644 --- a/samples/client/petstore/elixir/README.md +++ b/samples/client/petstore/elixir/README.md @@ -31,14 +31,14 @@ You can override the URL of your server (e.g. if you have a separate development configuration files). ```elixir -config :openapi_petstore, base_url: "http://petstore.swagger.io:80/v2" +config :openapi_petstore, base_url: "http://localhost/v2" ``` Multiple clients for the same API with different URLs can be created passing different `base_url`s when calling `OpenapiPetstore.Connection.new/1`: ```elixir -client = OpenapiPetstore.Connection.new(base_url: "http://petstore.swagger.io:80/v2") +client = OpenapiPetstore.Connection.new(base_url: "http://localhost/v2") ``` [exdoc]: https://github.com/elixir-lang/ex_doc diff --git a/samples/client/petstore/elixir/config/config.exs b/samples/client/petstore/elixir/config/config.exs index a6e7708580a..9ee2786bc49 100644 --- a/samples/client/petstore/elixir/config/config.exs +++ b/samples/client/petstore/elixir/config/config.exs @@ -7,7 +7,7 @@ # General application configuration import Config -config :openapi_petstore, base_url: "http://petstore.swagger.io:80/v2" +config :openapi_petstore, base_url: "http://localhost/v2" # Import environment specific config. This must remain at the bottom # of this file so it overrides the configuration defined above. diff --git a/samples/client/petstore/elixir/lib/openapi_petstore/connection.ex b/samples/client/petstore/elixir/lib/openapi_petstore/connection.ex index 060b01751ab..fed0639550d 100644 --- a/samples/client/petstore/elixir/lib/openapi_petstore/connection.ex +++ b/samples/client/petstore/elixir/lib/openapi_petstore/connection.ex @@ -8,19 +8,19 @@ defmodule OpenapiPetstore.Connection do Additional middleware can be set in the compile-time or runtime configuration: config :tesla, OpenapiPetstore.Connection, - base_url: "http://petstore.swagger.io:80/v2", + base_url: "http://localhost/v2", adapter: Tesla.Adapter.Hackney The default base URL can also be set as: config :openapi_petstore, - :base_url, "http://petstore.swagger.io:80/v2" + :base_url, "http://localhost/v2" """ @default_base_url Application.compile_env( :openapi_petstore, :base_url, - "http://petstore.swagger.io:80/v2" + "http://localhost/v2" ) @default_scopes [ diff --git a/samples/client/petstore/elixir/test/deserializer_test.exs b/samples/client/petstore/elixir/test/deserializer_test.exs index 31b2b2cf45f..d828974b7a5 100644 --- a/samples/client/petstore/elixir/test/deserializer_test.exs +++ b/samples/client/petstore/elixir/test/deserializer_test.exs @@ -30,7 +30,7 @@ defmodule DeserializerTest do """ test "jason_decode/2 with valid JSON" do - assert Deserializer.jason_decode(@valid_json, Pet) == + assert Deserializer.json_decode(@valid_json, Pet) == {:ok, %Pet{ id: 14, @@ -43,7 +43,7 @@ defmodule DeserializerTest do end test "jason_decode/2 with invalid JSON" do - assert Deserializer.jason_decode(~s/{: 1}/, Pet) == - {:error, %Jason.DecodeError{data: "{: 1}", position: 1, token: nil}} + assert Deserializer.json_decode(~s/{: 1}/, Pet) == + {:error, {:invalid_byte, 1, 58}} end end diff --git a/samples/client/petstore/elixir/test/format_test.exs b/samples/client/petstore/elixir/test/format_test.exs index 22e545db6b7..aa896b85522 100644 --- a/samples/client/petstore/elixir/test/format_test.exs +++ b/samples/client/petstore/elixir/test/format_test.exs @@ -33,8 +33,8 @@ defmodule FormatTest do string: "Hello world!", byte: "U3dhZ2dlciByb2Nrcw==", binary: <<1, 2, 3>>, - date: ~D[2013-10-20], - dateTime: ~U[2013-10-20T18:20:30Z], + date: "2013-10-20", + dateTime: "2013-10-20T19:20:30+01:00", uuid: "3fa85f64-5717-4562-b3fc-2c963f66afa6", password: "green?horse", pattern_with_digits: "1234567890", @@ -73,7 +73,7 @@ defmodule FormatTest do string: nil, byte: "U3dhZ2dlciByb2Nrcw==", binary: nil, - date: ~D[2013-10-20], + date: "2013-10-20", dateTime: nil, uuid: nil, password: "green?horse", diff --git a/samples/client/petstore/elixir/test/mixed_properties_and_additional_properties_class_test.exs b/samples/client/petstore/elixir/test/mixed_properties_and_additional_properties_class_test.exs index 94e18ee9345..1129a221749 100644 --- a/samples/client/petstore/elixir/test/mixed_properties_and_additional_properties_class_test.exs +++ b/samples/client/petstore/elixir/test/mixed_properties_and_additional_properties_class_test.exs @@ -15,7 +15,7 @@ defmodule MixedPropertiesAndAdditionalPropertiesClass do |> Model.decode() == %Model{ uuid: "3fa85f64-5717-4562-b3fc-2c963f66afa6", - dateTime: ~U[2013-10-20T18:20:30Z], + dateTime: "2013-10-20T19:20:30+01:00", map: %{ # TODO values should be Dog and Cat structs instead of an Animal "doggie" => %Animal{ diff --git a/samples/client/petstore/elixir/test/outer_enum_test.exs b/samples/client/petstore/elixir/test/outer_enum_test.exs index d8a6f38cf06..b193b5f8581 100644 --- a/samples/client/petstore/elixir/test/outer_enum_test.exs +++ b/samples/client/petstore/elixir/test/outer_enum_test.exs @@ -14,8 +14,8 @@ defmodule OuterEnumTest do """ @tag timeout: :infinity - test "jason_decode/2 with valid JSON" do - assert Deserializer.jason_decode(@valid_json, EnumTest) == + test "json_decode/2 with valid JSON" do + assert Deserializer.json_decode(@valid_json, EnumTest) == {:ok, %EnumTest{ enum_string: "UPPER", diff --git a/samples/client/petstore/elixir/test/pet_test.exs b/samples/client/petstore/elixir/test/pet_test.exs index 0c8743062db..54d848f0b78 100644 --- a/samples/client/petstore/elixir/test/pet_test.exs +++ b/samples/client/petstore/elixir/test/pet_test.exs @@ -24,17 +24,21 @@ defmodule PetTest do {:ok, %Tesla.Env{} = response} = PetApi.add_pet(connection, pet) assert response.status == 200 - {:ok, pet} = PetApi.get_pet_by_id(connection, petId) - assert pet.id == petId - assert pet.name == "elixir client test" - assert pet.photoUrls == ["http://test_elixir_unit_test.com"] - assert pet.category == %Category{id: petId, name: "test elixir category"} - assert pet.tags == [%Tag{:id => petId, :name => "test elixir tag"}] + retry_assert(fn -> + {:ok, pet} = PetApi.get_pet_by_id(connection, petId) + assert pet.id == petId + assert pet.name == "elixir client test" + assert pet.photoUrls == ["http://test_elixir_unit_test.com"] + assert pet.category == %Category{id: petId, name: "test elixir category"} + assert pet.tags == [%Tag{:id => petId, :name => "test elixir tag"}] + end) {:ok, response} = PetApi.delete_pet(connection, petId) assert response.status == 200 - {:ok, response} = PetApi.get_pet_by_id(connection, petId) - assert response.status == 404 + retry_assert(fn -> + {:ok, response} = PetApi.get_pet_by_id(connection, petId) + assert response.status == 404 + end) end test "update a pet", %{connection: connection} do @@ -50,10 +54,24 @@ defmodule PetTest do {:ok, response} = PetApi.update_pet(connection, pet) assert response.status == 200 - {:ok, pet} = PetApi.get_pet_by_id(connection, petId) - assert pet.id == petId - assert pet.name == "elixir client updatePet" - assert pet.status == "pending" + retry_assert(fn -> + {:ok, pet} = PetApi.get_pet_by_id(connection, petId) + assert pet.id == petId + assert pet.name == "elixir client updatePet" + assert pet.status == "pending" + end, 5, 100) + end + + def retry_assert(fun, attempts \\ 3, delay \\ 100) + def retry_assert(_fun, 0, _delay), do: flunk("assertion failed after retries") + def retry_assert(fun, attempts, delay) do + try do + fun.() + rescue + _e -> + Process.sleep(delay) + retry_assert(fun, attempts - 1, delay) + end end test "find pet by status", %{connection: connection} do