Add workflow to test Elixir clients (#21214)

* 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 <enrique@bluelabs.eu>
This commit is contained in:
William Cheng 2025-05-05 15:32:05 +08:00 committed by GitHub
parent 41012588dd
commit 2010c2a60a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 88 additions and 27 deletions

39
.github/workflows/samples-elixir.yaml vendored Normal file
View File

@ -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 }}

View File

@ -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:

View File

@ -21,3 +21,4 @@
#docs/*.md
# Then explicitly reverse the ignore rule for a single file:
#!docs/README.md
#

View File

@ -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

View File

@ -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.

View File

@ -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 [

View File

@ -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

View File

@ -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",

View File

@ -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{

View File

@ -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",

View File

@ -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