---
title: Migrating a LoadRunner Script to OctoPerf With an AI Agent
url: https://blog.octoperf.com/migrating-a-loadrunner-script-to-octoperf-with-an-ai-agent/
date: '2026-08-25'
authors:
- gpereira
categories:
- Tutorials
tags:
- Artificial Intelligence
- Migration
- Design
- HTTP
description: 'A VuGen script migrated to OctoPerf from a chat: survey the C, read the portability verdict, import the folder, then finish by hand what the C left behind. Real prompts, real results, and the script to download.'
---

# Migrating a LoadRunner Script to OctoPerf With an AI Agent

A VuGen script is C. Some scripts are a recorded journey with helpers around it, others are a framework that happens to send a few requests. OctoPerf 17 ships a **LoadRunner migration playbook** for AI agents whose first job is to tell you which one you have.

Real prompts, real output, **script downloadable**.


## Table of Contents

- [The Script](#the-script)
- [Step 1: Survey](#survey-before-importing)
- [Step 2: Import](#import)
- [Step 3: Work the Unconverted List](#unconverted-list)
    - [The C That Computed a Value](#custom-c)
    - [The Condition That Lost Its Guard](#condition)
- [Step 4: Validate](#validate)
- [What This Saves](#what-it-saves)
- [Conclusion](#conclusion)

## The Script { #the-script }

Target: **[JPetStore](https://petstore.octoperf.com/)**, our public MyBatis demo shop. Every step is reproducible.

![The VuGen script, its Step Navigator and a green replay](https://blog.octoperf.com/img/blog/migrate-loadrunner-project-with-ai/vugen-step-navigator.png "Eight transactions, 99 steps, replay passed")

One `Action.c`, HTTP recording level, eight transactions walking a purchase. Around them:

- **hand-written C** computing which item the user buys,
- a **condition reading a C local**, guarding a fallback request,
- a **parameterised server**,
- a **recorded cookie**, re-sent after the cookie jar is emptied,
- `.prm` parameters backed by `.dat` files.

![The Parameter List, with host, Password and Username](https://blog.octoperf.com/img/blog/migrate-loadrunner-project-with-ai/vugen-parameter-list.png "Three File parameters, host among them")

It replays green in VuGen. **[Download it](https://blog.octoperf.com/img/blog/migrate-loadrunner-project-with-ai/jp-14-custom-c.zip)** to follow along.

## Step 1: Survey { #survey-before-importing }

The first prompt is not an import.

> Here is a LoadRunner script folder. Before importing anything, tell me whether it is worth migrating.

**Nothing is created.** Trimmed answer:

```json
"portability": "ASSISTED",
"protocols": [ { "protocol": "QTWeb", "scripts": 1, "convertible": true } ],
"inventory": {
  "calls": 100, "pages": 8, "transactions": 8, "actions": 3,
  "extractors": 3, "assertions": 3, "parameters": 3,
  "dataFiles": 2, "unsupportedCalls": 3
}
```

Three verdicts:

- **`MECHANICAL`**, plain HTTP, runs nearly as it is,
- **`ASSISTED`**, HTTP plus C helpers or control flow, import and work the list,
- **`MOSTLY_MANUAL`**, the C outweighs the requests.

**`MOSTLY_MANUAL` is not a difficulty rating, it is a different script.** Plenty of authors built a whole framework in a `.c`: their calls are `getenv`, `sprintf`, `strtok` and their own helpers. Nothing there converts, because nothing there is a request.

Two habits. **A script can declare `QTWeb` and send nothing at all**, so read the inventory, not the protocol. And read the per-script breakdown: an upload can be `MOSTLY_MANUAL` overall while three of its six scripts are plain HTTP.

## Step 2: Import { #import }

> Import it into a new project.

![The imported tree, with its markers and the sanity check](https://blog.octoperf.com/img/blog/migrate-loadrunner-project-with-ai/octoperf-imported-tree-markers.png "The eight transactions became containers, and the C left two empty markers")

Transactions became named containers, `web_reg_find` became assertions, `.prm` parameters became variables with their `.dat` uploaded, the parameterised server became `https://${host}:443`, and the recorded cookie became a `Cookie` header.

**Zip the whole folder**, not the `Action.c`. `data/` is not optional: an HTML-level recording carries no URLs of its own, so without its snapshots every click imports as a request against the entry page. Drop `result*/`, `data/http.db/` and the replay logs.

The sanity check already flags **two `ContainerAction is empty`**: the markers the C left behind.

## Step 3: Work the Unconverted List { #unconverted-list }

> Work the unconverted list. Explain each entry before you fix it.

**Work from the list, not from the tree.** Each entry carries the script, the action and **the line of the `.c`**, which is what makes it actionable in a two-thousand-line file.

### The C That Computed a Value { #custom-c }

![The C block in VuGen](https://blog.octoperf.com/img/blog/migrate-loadrunner-project-with-ai/vugen-custom-c-block.png "Three lines of C: rand, sprintf, lr_save_string")

![The Runtime Data panel in VuGen](https://blog.octoperf.com/img/blog/migrate-loadrunner-project-with-ai/vugen-runtime-data.png "itemId resolved at replay, alongside host and the correlated values")

Three entries, one story: `rand` line 300, `sprintf` line 301, the parameter they feed line 302. `lr_save_string` converts on its own, so what gets reported is the arithmetic feeding it.

Three markers, one Groovy action:

```groovy
def items = ["EST-1", "EST-2"]
vars.put("itemId", items[new Random().nextInt(items.size())])
```

Other shapes of C: one that **wrapped web calls** produced no request, so rebuild or re-record. One that **logged or counted** is dropped silently. One that **paused** leaves you choosing the number, since the argument was a C local.

### The Condition That Lost Its Guard { #condition }

![The condition in VuGen, reading a C local](https://blog.octoperf.com/img/blog/migrate-loadrunner-project-with-ai/vugen-condition-on-c-local.png "rc holds the return code, and the guard reads it")

**The steps are in the tree, the guard is not.** The fallback sits inside its container, the original condition survives in the node name, but the container has no working guard until you give it one.

**Three C shapes convert**, all three reading a LoadRunner parameter:

```c
strcmp(lr_eval_string("{status}"), "OK") == 0
atoi(lr_eval_string("{count}")) >= 1
strstr(lr_eval_string("{body}"), "token") != NULL
```

Everything else reads a C local. Ours is `rc != LR_PASS`, the guard you meet most often, and **nothing on the OctoPerf side stands for "my helper failed"**. Say what you meant:

```
${__groovy(!prev.isSuccessful())}
```

## Step 4: Validate { #validate }

> Validate the Virtual User.

Validation replays the Virtual User on a real load generator and **costs no test hours**.

The first run fails on the markers, and everything downstream fails with them: no item picker, no item page. One root cause with a tail.

After the list, the same Virtual User validates **32 out of 32**.

![The Virtual User after the fixes](https://blog.octoperf.com/img/blog/migrate-loadrunner-project-with-ai/octoperf-after-fixes-green.png "Sanity check succeeded, validation passed")

The request count went *down* between the two runs: the fallback now fires only when the sign-in actually fails. **The count dropped because the test became correct.**

## What This Saves { #what-it-saves }

The agent invented nothing. Requests, transactions, headers, think times, parameters and data files converted mechanically.

What it added: **knowing what to do with what did not convert**, in the right order, and seeing that three entries were one story told three times.

The caveat: **it depends entirely on the verdict**. `ASSISTED` is where an agent shines. On a `MOSTLY_MANUAL` script, the useful answer comes from the survey in the first minute, and it is *do not*.

## Conclusion { #conclusion }

Four prompts: survey, import, work the list, validate. The [script](https://blog.octoperf.com/img/blog/migrate-loadrunner-project-with-ai/jp-14-custom-c.zip) and JPetStore are public, so nothing here needs to be taken on trust.

Moving off [LoadRunner](https://octoperf.com/loadrunner-alternative/)? Run the survey on your worst script, not your best. It writes nothing, costs nothing, and the verdict is the only part of a migration plan that is hard to guess.

The [NeoLoad walkthrough](https://blog.octoperf.com/migrating-a-neoload-project-to-octoperf-with-an-ai-agent/) follows the same path, scenario included.
