From 2958b1eb32972d658dcd3b0edd49df6f6069ce9f Mon Sep 17 00:00:00 2001 From: Mirek Kratochvil Date: Sat, 28 Oct 2023 17:19:44 +0200 Subject: [PATCH 01/10] be a bit more generous about versions --- Project.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Project.toml b/Project.toml index 9775f33..365dcc3 100644 --- a/Project.toml +++ b/Project.toml @@ -11,8 +11,8 @@ ReadableRegex = "cbbcb084-453d-4c4c-b292-e315607ba6a4" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" [compat] -AbstractFBCModels = "0.2" -DocStringExtensions = "0.9" +AbstractFBCModels = "0.1, 0.2" +DocStringExtensions = "0.8, 0.9" JSON = "0.21" [extras] From 74ecf81dc5066ec2a7713abe4500a13aaa8af600 Mon Sep 17 00:00:00 2001 From: Mirek Kratochvil Date: Sat, 28 Oct 2023 17:22:07 +0200 Subject: [PATCH 02/10] clean up README (refer to precise docs instead of leaving open explanations) --- README.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 6bb9e08..4574e60 100644 --- a/README.md +++ b/README.md @@ -18,18 +18,19 @@ The primary purpose of this is to provide JSON loading functionality for [FBCModelTests.jl](https://github.com/LCSB-BioCore/FBCModelTests.jl), but is otherwise completely generic and can be used independently of these packages. -You should be able to load JSON models (called `JSONFBCModel`) via the +You should be able to load JSON models (of type `JSONFBCModel`) via the AbstractFBCModels interface: ```julia -import AbstractFBCModels as A +import AbstractFBCModels as M import JSONFBCModels -model = A.load("my_model.json") -A.reactions(model) # accessors defined by the interface (NB: access through the overloads: A.xyz) +model = M.load("my_model.json") ``` -See the docstrings of all the accessors available by entering `A.accessors()` in -the REPL. For example: + +Documentation of +[AbstractFBCModels.jl](https://github.com/COBREXA/AbstractFBCModels.jl) +provides details on the use of the loaded model. #### Acknowledgements From d68a51082608c803fface04bb086ac12e8014650 Mon Sep 17 00:00:00 2001 From: Mirek Kratochvil Date: Sat, 28 Oct 2023 17:23:09 +0200 Subject: [PATCH 03/10] juliaformatter --- src/utils.jl | 4 ++-- test/io.jl | 2 +- test/runtests.jl | 9 +++++++-- test/test_utils.jl | 4 +++- 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/utils.jl b/src/utils.jl index db0e160..f66b995 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -8,7 +8,7 @@ _json_gene_name(g, i) = string(get(g, "id", "gene$i")) function parse_grr(str::Maybe{String}) isnothing(str) && return nothing isempty(str) && return nothing - + dnf = A.GeneAssociationDNF() for isozyme in string.(split(str, " or ")) push!( @@ -53,7 +53,7 @@ function parse_annotations_or_notes(x) if isa(vs, String) a_or_n[k] = String[vs] else - a_or_n[k] = String[v for v in vs] + a_or_n[k] = String[v for v in vs] end end return a_or_n diff --git a/test/io.jl b/test/io.jl index 45604a0..1d16039 100644 --- a/test/io.jl +++ b/test/io.jl @@ -22,5 +22,5 @@ saved_path = joinpath(mktempdir(), "test-model.json") A.save(model, saved_path) - test_iml1515_details(saved_path) + test_iml1515_details(saved_path) end diff --git a/test/runtests.jl b/test/runtests.jl index be82fd4..16b18f2 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -24,10 +24,15 @@ iml1515_path = A.download_data_file( end @testset "Accessors" begin - A.run_fbcmodel_file_tests(J.JSONFBCModel, iml1515_path; name="iML1515", test_save=true) + A.run_fbcmodel_file_tests( + J.JSONFBCModel, + iml1515_path; + name = "iML1515", + test_save = true, + ) @testset "Specific details of iML1515" begin - test_iml1515_details(iml1515_path) + test_iml1515_details(iml1515_path) end end end diff --git a/test/test_utils.jl b/test/test_utils.jl index 08825c2..1358ba6 100644 --- a/test/test_utils.jl +++ b/test/test_utils.jl @@ -11,7 +11,9 @@ function test_iml1515_details(iml1515_path::String) @test all(length.(A.bounds(model)) .== 2712) @test all(A.balance(model) .== 0) @test A.objective(model)[2669] == 1 - @test all(in.(A.reaction_gene_association_dnf(model, "FBA"), Ref([["b2925"], ["b2097"]]))) + @test all( + in.(A.reaction_gene_association_dnf(model, "FBA"), Ref([["b2925"], ["b2097"]])), + ) @test A.metabolite_formula(model, "atp_c")["C"] == 10 @test A.metabolite_charge(model, "atp_c") == -4 @test A.metabolite_compartment(model, "atp_c") == "c" From d3ec509a73b80d2f81a7fbfb311c0973126b9b77 Mon Sep 17 00:00:00 2001 From: Mirek Kratochvil Date: Sat, 28 Oct 2023 17:24:26 +0200 Subject: [PATCH 04/10] remove leftover --- test/utils.jl | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 test/utils.jl diff --git a/test/utils.jl b/test/utils.jl deleted file mode 100644 index 492d588..0000000 --- a/test/utils.jl +++ /dev/null @@ -1,4 +0,0 @@ -str = "(b3739 and (b3731 and b3733 and b3735 and b3734 and b3732) and (b3738 and b3736 and b3737)) or ((b3731 and b3733 and b3735 and b3734 and b3732) and (b3738 and b3736 and b3737))" -str = "b3731 and b3733" -str = "b3731 or b3733" -str = "b4562" From 7130b24f9f07144e9461aafd7c602249b2dd84e4 Mon Sep 17 00:00:00 2001 From: Mirek Kratochvil Date: Sat, 28 Oct 2023 17:27:06 +0200 Subject: [PATCH 05/10] clean up gitignore --- .gitignore | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index f0a86a3..e4e71a6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,10 @@ -/.vscode -/vscode -*.code-workspace +/docs/build/ Manifest.toml -downloaded/* -test/downloaded/* \ No newline at end of file +*.cov +.*.swp + +.vscode +*.code-workspace + +/test/test-models From 4b316ab2ab2df3c31c5542dba14afa504d23047b Mon Sep 17 00:00:00 2001 From: Mirek Kratochvil Date: Sat, 28 Oct 2023 17:27:58 +0200 Subject: [PATCH 06/10] add github CI --- .github/workflows/CompatHelper.yml | 43 +++++++++++++++++++++ .github/workflows/TagBot.yml | 15 ++++++++ .github/workflows/ci.yml | 42 ++++++++++++++++++++ .github/workflows/pr-format.yml | 62 ++++++++++++++++++++++++++++++ 4 files changed, 162 insertions(+) create mode 100644 .github/workflows/CompatHelper.yml create mode 100644 .github/workflows/TagBot.yml create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/pr-format.yml diff --git a/.github/workflows/CompatHelper.yml b/.github/workflows/CompatHelper.yml new file mode 100644 index 0000000..8765dd6 --- /dev/null +++ b/.github/workflows/CompatHelper.yml @@ -0,0 +1,43 @@ +name: CompatHelper +on: + schedule: + - cron: 0 0 * * * + workflow_dispatch: +permissions: + contents: write + pull-requests: write +jobs: + CompatHelper: + runs-on: ubuntu-latest + steps: + - name: Check if Julia is already available in the PATH + id: julia_in_path + run: which julia + continue-on-error: true + - name: Install Julia, but only if it is not already available in the PATH + uses: julia-actions/setup-julia@v1 + with: + version: '1' + arch: ${{ runner.arch }} + if: steps.julia_in_path.outcome != 'success' + - name: "Add the General registry via Git" + run: | + import Pkg + ENV["JULIA_PKG_SERVER"] = "" + Pkg.Registry.add("General") + shell: julia --color=yes {0} + - name: "Install CompatHelper" + run: | + import Pkg + name = "CompatHelper" + uuid = "aa819f21-2bde-4658-8897-bab36330d9b7" + version = "3" + Pkg.add(; name, uuid, version) + shell: julia --color=yes {0} + - name: "Run CompatHelper" + run: | + import CompatHelper + CompatHelper.main() + shell: julia --color=yes {0} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/TagBot.yml b/.github/workflows/TagBot.yml new file mode 100644 index 0000000..f49313b --- /dev/null +++ b/.github/workflows/TagBot.yml @@ -0,0 +1,15 @@ +name: TagBot +on: + issue_comment: + types: + - created + workflow_dispatch: +jobs: + TagBot: + if: github.event_name == 'workflow_dispatch' || github.actor == 'JuliaTagBot' + runs-on: ubuntu-latest + steps: + - uses: JuliaRegistries/TagBot@v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} + ssh: ${{ secrets.DOCUMENTER_KEY }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..a74714a --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,42 @@ +name: CI + +on: + push: + branches: + - master + pull_request: + +jobs: + test: + name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + version: + - '1' # This is always the latest stable release in the 1.X series + #- '1.6' # LTS + #- 'nightly' + os: + - ubuntu-latest + #- macOS-latest + #- windows-latest + arch: + - x64 + steps: + - uses: actions/checkout@v4 + - uses: julia-actions/setup-julia@v1 + with: + version: ${{ matrix.version }} + arch: ${{ matrix.arch }} + - uses: julia-actions/cache@v1 + - uses: julia-actions/julia-buildpkg@latest + - run: | + git config --global user.name Tester + git config --global user.email te@st.er + - uses: julia-actions/julia-runtest@latest + continue-on-error: ${{ matrix.version == 'nightly' }} + - uses: julia-actions/julia-processcoverage@v1 + - uses: codecov/codecov-action@v1 + with: + file: lcov.info diff --git a/.github/workflows/pr-format.yml b/.github/workflows/pr-format.yml new file mode 100644 index 0000000..3ff70d1 --- /dev/null +++ b/.github/workflows/pr-format.yml @@ -0,0 +1,62 @@ +on: + pull_request: + issue_comment: + types: [created] + +name: Formatting + +jobs: + formatting: + if: github.event_name == 'pull_request' || (github.event_name == 'issue_comment' && github.event.issue.pull_request && (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'COLLABORATOR' || github.event.comment.author_association == 'OWNER' || github.event.issue.user.id == github.event.comment.user.id) && startsWith(github.event.comment.body, '/format') ) + runs-on: ubuntu-latest + steps: + - name: Clone the repository + uses: actions/checkout@v4 + - name: Checkout the pull request code # this checks out the actual branch so that one can commit into it + if: github.event_name == 'issue_comment' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh pr checkout ${{ github.event.issue.number }} + - name: Install JuliaFormatter and format + run: | + julia --color=yes -e 'import Pkg; Pkg.add("JuliaFormatter")' + julia --color=yes -e 'using JuliaFormatter; format(".")' + - name: Remove trailing whitespace + run: | + find -name '*.jl' -or -name '*.md' -or -name '*.toml' -or -name '*.yml' | while read filename ; do + # remove any trailing spaces + sed --in-place -e 's/\s*$//' "$filename" + # add a final newline if missing + if [[ -s "$filename" && $(tail -c 1 "$filename" |wc -l) -eq 0 ]] ; then + echo >> "$filename" + fi + # squash superfluous final newlines + sed -i -e :a -e '/^\n*$/{$d;N;};/\n$/ba' "$filename" + done + - name: Fail on formatting problems + if: github.event_name == 'pull_request' + run: | + if git diff --exit-code --quiet + then echo "Looks OK" + else echo "Formatting fixes required!"; git diff -p ; exit 1 + fi + - name: Commit fixes + if: github.event_name == 'issue_comment' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + if [ `git status -s | wc -l` -ne 0 ] ; then + git config --local user.name "$GITHUB_ACTOR" + git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" + git commit -a -m "automatic formatting" -m "triggered by @$GITHUB_ACTOR on PR #${{ github.event.issue.number }}" + if git push + then gh pr comment ${{ github.event.issue.number }} --body \ + ":heavy_check_mark: Auto-formatting triggered by [this comment](${{ github.event.comment.html_url }}) succeeded, commited as `git rev-parse HEAD`" + else gh pr comment ${{ github.event.issue.number }} --body \ + ":x: Auto-formatting triggered by [this comment](${{ github.event.comment.html_url }}) failed, perhaps someone pushed to the PR in the meantime?" + fi + else + gh pr comment ${{ github.event.issue.number }} --body \ + ":sunny: Auto-formatting triggered by [this comment](${{ github.event.comment.html_url }}) succeeded, but the code was already formatted correctly." + fi From f653eb8dc71ffc195d0ba54dc0856e9b573531a4 Mon Sep 17 00:00:00 2001 From: Mirek Kratochvil Date: Sat, 28 Oct 2023 17:41:13 +0200 Subject: [PATCH 07/10] clean up tests, add reaction_stoichiometry type annotation --- src/interface.jl | 2 +- test/io.jl | 26 ---------- test/misc.jl | 13 +++++ test/runtests.jl | 65 +++++++++++++------------ test/{test_utils.jl => test_iML1515.jl} | 7 ++- 5 files changed, 52 insertions(+), 61 deletions(-) delete mode 100644 test/io.jl create mode 100644 test/misc.jl rename test/{test_utils.jl => test_iML1515.jl} (87%) diff --git a/src/interface.jl b/src/interface.jl index 6e8bb06..ae49188 100644 --- a/src/interface.jl +++ b/src/interface.jl @@ -169,7 +169,7 @@ A.metabolite_name(model::JSONFBCModel, mid::String) = A.gene_name(model::JSONFBCModel, gid::String) = parse_name(get(model.genes[model.gene_index[gid]], "name", nothing)) -A.reaction_stoichiometry(model::JSONFBCModel, rid::String) = +A.reaction_stoichiometry(model::JSONFBCModel, rid::String)::Dict{String,Float64} = model.reactions[model.reaction_index[rid]]["metabolites"] function A.metabolite_compartment(model::JSONFBCModel, mid::String) diff --git a/test/io.jl b/test/io.jl deleted file mode 100644 index 1d16039..0000000 --- a/test/io.jl +++ /dev/null @@ -1,26 +0,0 @@ -@testset "IO" begin - model = A.load(J.JSONFBCModel, iml1515_path) - - @test all( - in.( - keys(model.json), - Ref([ - "metabolites" - "id" - "compartments" - "reactions" - "version" - "genes" - ]), - ), - ) - - @test all(in.(A.filename_extensions(J.JSONFBCModel), Ref([ - "json" - "JSON" - ]))) - - saved_path = joinpath(mktempdir(), "test-model.json") - A.save(model, saved_path) - test_iml1515_details(saved_path) -end diff --git a/test/misc.jl b/test/misc.jl new file mode 100644 index 0000000..eb87142 --- /dev/null +++ b/test/misc.jl @@ -0,0 +1,13 @@ + +@testset "Miscellaneous tests" begin + # the path is inherited from the main testset + model = A.load(JSONFBCModel, joinpath(@__DIR__, "test-models", "iML1515.json")) + # test that no superfluous keys are generated + @test all( + key in ["metabolites", "id", "compartments", "reactions", "version", "genes"] for + key in keys(model.json) + ) + + # caps suffix is quite common + @test all(suffix in A.filename_extensions(JSONFBCModel) for suffix in ["json", "JSON"]) +end diff --git a/test/runtests.jl b/test/runtests.jl index 16b18f2..206ebe6 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,38 +1,39 @@ -using Test import AbstractFBCModels as A -import JSONFBCModels as J - -include("test_utils.jl") - -isdir("downloaded") || mkdir("downloaded") +import JSONFBCModels: JSONFBCModel -iml1515_path = A.download_data_file( - "http://bigg.ucsd.edu/static/models/iML1515.json", - joinpath("downloaded", "iML1515.json"), - "b0f9199f048779bb08a14dfa6c09ec56d35b8750d2f99681980d0f098355fbf5", -) - -@testset "JSONFBCModels" begin - @testset "Interface implemented" begin - A.run_fbcmodel_type_tests(J.JSONFBCModel) - end - - @testset "IO" begin - include("io.jl") - # TODO test convert +using Test +@testset "JSONFBCModels tests" begin + A.run_fbcmodel_type_tests(JSONFBCModel) + + modeldir = joinpath(@__DIR__, "test-models") + mkpath(modeldir) + + for (name, url, hash, ts) in [ + ( + "e_coli_core", + "http://bigg.ucsd.edu/static/models/e_coli_core.json", + "7bedec10576cfe935b19218dc881f3fb14f890a1871448fc19a9b4ee15b448d8", + true, + ), + ( + "iJO1366", + "http://bigg.ucsd.edu/static/models/iJO1366.json", + "9376a93f62ad430719f23e612154dd94c67e0d7c9545ed9d17a4d0c347672313", + true, + ), + ( + "iML1515", + "http://bigg.ucsd.edu/static/models/iML1515.json", + "b0f9199f048779bb08a14dfa6c09ec56d35b8750d2f99681980d0f098355fbf5", + true, + ), + ] + path = joinpath(modeldir, "$name.json") + A.download_data_file(url, path, hash) + A.run_fbcmodel_file_tests(JSONFBCModel, path; name, test_save = ts) end - @testset "Accessors" begin - A.run_fbcmodel_file_tests( - J.JSONFBCModel, - iml1515_path; - name = "iML1515", - test_save = true, - ) - - @testset "Specific details of iML1515" begin - test_iml1515_details(iml1515_path) - end - end + include("test_iML1515.jl") + include("misc.jl") end diff --git a/test/test_utils.jl b/test/test_iML1515.jl similarity index 87% rename from test/test_utils.jl rename to test/test_iML1515.jl index 1358ba6..4aea939 100644 --- a/test/test_utils.jl +++ b/test/test_iML1515.jl @@ -1,5 +1,8 @@ -function test_iml1515_details(iml1515_path::String) - model = A.load(J.JSONFBCModel, iml1515_path) + +@testset "Test the expected contents of iML1515.json" begin + + # the path is inherited from the main testset + model = A.load(JSONFBCModel, joinpath(@__DIR__, "test-models", "iML1515.json")) @test "SHK3Dr" in A.reactions(model) @test A.n_reactions(model) == 2712 From 996d2b1a266bb14676f735539dc03989493acc5e Mon Sep 17 00:00:00 2001 From: Mirek Kratochvil Date: Sat, 28 Oct 2023 17:48:31 +0200 Subject: [PATCH 08/10] squash trivial case --- src/interface.jl | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/interface.jl b/src/interface.jl index ae49188..be9d2ab 100644 --- a/src/interface.jl +++ b/src/interface.jl @@ -174,11 +174,7 @@ A.reaction_stoichiometry(model::JSONFBCModel, rid::String)::Dict{String,Float64} function A.metabolite_compartment(model::JSONFBCModel, mid::String) x = get(model.metabolites[model.metabolite_index[mid]], "compartment", nothing) - if isa(x, String) - return x - else - return nothing - end + return isa(x, String) ? x : nothing end function Base.convert(::Type{JSONFBCModel}, mm::A.AbstractFBCModel) From 7f378acc1accff05ca38eb32f9fde22fbb30df3e Mon Sep 17 00:00:00 2001 From: Mirek Kratochvil Date: Sat, 28 Oct 2023 17:50:49 +0200 Subject: [PATCH 09/10] precise identifiers --- src/interface.jl | 12 ++++++------ src/utils.jl | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/interface.jl b/src/interface.jl index be9d2ab..1ac6d0b 100644 --- a/src/interface.jl +++ b/src/interface.jl @@ -47,28 +47,28 @@ JSONFBCModel(json::Dict{String,Any}) = begin JSONFBCModel( json, - Dict(_json_rxn_name(r, i) => i for (i, r) in enumerate(rs)), + Dict(extract_json_reaction_id(r, i) => i for (i, r) in enumerate(rs)), rs, - Dict(_json_met_name(m, i) => i for (i, m) in enumerate(ms)), + Dict(extract_json_metabolite_id(m, i) => i for (i, m) in enumerate(ms)), ms, - Dict(_json_gene_name(g, i) => i for (i, g) in enumerate(gs)), + Dict(extract_json_gene_id(g, i) => i for (i, g) in enumerate(gs)), gs, ) end # model A.reactions(model::JSONFBCModel) = - String[_json_rxn_name(r, i) for (i, r) in enumerate(model.reactions)] + String[extract_json_reaction_id(r, i) for (i, r) in enumerate(model.reactions)] A.n_reactions(model::JSONFBCModel) = length(model.reactions) A.metabolites(model::JSONFBCModel) = - String[_json_met_name(m, i) for (i, m) in enumerate(model.metabolites)] + String[extract_json_metabolite_id(m, i) for (i, m) in enumerate(model.metabolites)] A.n_metabolites(model::JSONFBCModel) = length(model.metabolites) A.genes(model::JSONFBCModel) = - String[_json_gene_name(g, i) for (i, g) in enumerate(model.genes)] + String[extract_json_gene_id(g, i) for (i, g) in enumerate(model.genes)] A.n_genes(model::JSONFBCModel) = length(model.genes) diff --git a/src/utils.jl b/src/utils.jl index f66b995..fcb7bc6 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -1,9 +1,9 @@ -_json_rxn_name(r, i) = string(get(r, "id", "rxn$i")) +extract_json_reaction_id(r, i) = string(get(r, "id", "rxn$i")) -_json_met_name(m, i) = string(get(m, "id", "met$i")) +extract_json_metabolite_id(m, i) = string(get(m, "id", "met$i")) -_json_gene_name(g, i) = string(get(g, "id", "gene$i")) +extract_json_gene_id(g, i) = string(get(g, "id", "gene$i")) function parse_grr(str::Maybe{String}) isnothing(str) && return nothing From dd6e63bd9c3c13580be9226f54a13e8b6ac7fa3f Mon Sep 17 00:00:00 2001 From: Mirek Kratochvil Date: Sat, 28 Oct 2023 18:31:28 +0200 Subject: [PATCH 10/10] add corner case tests this hits 100% --- src/utils.jl | 12 +++++++----- test/misc.jl | 12 ++++++++++++ 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/utils.jl b/src/utils.jl index fcb7bc6..264d6cd 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -31,15 +31,17 @@ function parse_formula(x::Maybe{String}) return res end -function parse_charge(x) +function parse_charge(x)::Maybe{Int} if isa(x, Int) - return x + x elseif isa(x, Float64) - return Int(x)::Int + Int(x) elseif isa(x, String) - return parse(Int, x) - else + Int(parse(Float64, x)) + elseif isnothing(x) nothing + else + throw(DomainError(x, "cannot parse charge")) end end diff --git a/test/misc.jl b/test/misc.jl index eb87142..0629410 100644 --- a/test/misc.jl +++ b/test/misc.jl @@ -11,3 +11,15 @@ # caps suffix is quite common @test all(suffix in A.filename_extensions(JSONFBCModel) for suffix in ["json", "JSON"]) end + +@testset "Corner cases" begin + import JSONFBCModels: parse_charge + + @test parse_charge(1) == 1 + @test parse_charge(2.0) == 2 + @test parse_charge("3") == 3 + @test parse_charge("4.0") == 4 + @test parse_charge(nothing) == nothing + @test_throws ArgumentError parse_charge("totally positive charge") + @test_throws DomainError parse_charge(["very charged"]) +end